# Advanced Type Design

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Types and OOP
Source: https://www.devtutorials.ai/learn/go/types-and-oop/advanced-type-design

> Custom types with the type keyword, type aliases, methods on any named type, method sets and their rules, and composing interfaces from smaller ones.

## What Is Type Design?

**Type design** = choosing and building the right types for your program's data.

So far you have used Go's types (`int`, `string`) and your own structs.
This lesson adds three more tools:

- **custom types:** a new type built on an existing one, like `Celsius` built on `float64`
- **method sets:** the rules for which methods a type and its pointer have
- **embedded interfaces:** bigger interfaces built from smaller ones

**Simple lesson:** good types make wrong code hard to write.

### A Real-World Example

The school nurse keeps two numbers for every student:

- body temperature, in °C, like `37.5`
- weight, in kg, like `37.5`

Both are numbers. But adding a temperature to a weight makes no sense.

Now the mapping:

- If both are just `float64`, Go cannot stop the mix-up.
- If one is `Celsius` and the other is `Kilograms`, Go refuses to add them.
- The types carry the **meaning**, and Go checks it.

This lesson builds on three earlier lessons:

- types and conversion from **Data Types**
- methods and receivers from **Structs and Methods**
- interfaces from **Interfaces and Polymorphism**

## What You Will Learn

In this lesson, you will learn:

1. The `type` keyword, custom types and type aliases.
2. How to add methods to your own types.
3. What a method set is, and the difference between value and pointer methods.
4. The rules for which methods are available.
5. How to build interfaces from smaller interfaces.

## Type Definitions

### type Keyword

**Type definition** = `type NewName BaseType`. It makes a **brand-new** type.

- The new type has the same shape as the base type.
- But Go treats it as a **different** type.
- You can add methods to it.

```go
type Celsius float64
type Kilograms float64
type Grade string
type Marks []int
```

### Custom Types

```go runnable expect-error title="Go stops the mix-up"
package main

import "fmt"

type Celsius float64
type Kilograms float64

func main() {
	var temp Celsius = 37.5
	var weight Kilograms = 37.5
	fmt.Println(temp + weight)
}
```

- Go refuses to build: `mismatched types Celsius and Kilograms`.
- That mistake is caught before the program ever runs.

To mix them on purpose, convert: `float64(temp) + float64(weight)`. See **Data Types**.

### Methods on Custom Types

Any type you define in your package can have methods, not only structs.

```go runnable title="Methods on a float and a slice type"
package main

import "fmt"

type Celsius float64

func (c Celsius) Fahrenheit() float64 {
	return float64(c)*9/5 + 32
}

func (c Celsius) HasFever() bool {
	return c >= 38
}

type Marks []int

func (m Marks) Average() float64 {
	total := 0
	for _, x := range m {
		total += x
	}
	return float64(total) / float64(len(m))
}

func main() {
	t := Celsius(38.5)
	fmt.Println(t.Fahrenheit(), t.HasFever())

	m := Marks{80, 90, 70}
	fmt.Println(m.Average(), len(m))
}
```

```text
101.3 true
80 3
```

- `Marks` is still a slice: `len(m)` and `range` work on it.
- But now it also has an `Average()` method.

### Named Constants for a Custom Type

```go runnable title="A Grade type with fixed values"
package main

import "fmt"

type Grade string

const (
	GradeA Grade = "A"
	GradeB Grade = "B"
	GradeC Grade = "C"
)

func (g Grade) Message() string {
	switch g {
	case GradeA:
		return "Excellent"
	case GradeB:
		return "Good"
	default:
		return "Keep practising"
	}
}

func main() {
	var g Grade = GradeB
	fmt.Println(g, g.Message())
}
```

```text
B Good
```

- Functions that take a `Grade` make the meaning clear: `func Award(g Grade)`.
- You can also number such constants with `iota`. See **Variables and Constants**.

### Type Aliases

**Type alias** = `type NewName = ExistingType`. It is just a **second name** for the same type.

```go runnable title="An alias is the same type"
package main

import "fmt"

type RollNo = int

func main() {
	var r RollNo = 7
	var n int = r
	fmt.Println(n + 1)

	var b byte = 65
	var u uint8 = b
	fmt.Println(u)
}
```

```text
8
65
```

- `RollNo` and `int` mix freely, because they are one type.
- Go's own `byte` is an alias for `uint8`, and `rune` for `int32`.

**Simple lesson:** `type X Y` makes a new type with its own meaning, and `type X = Y` is only a second name.

## Method Sets

**Method set** = the list of methods that a type has.

It decides which interfaces a type fits.

### Value Methods

**Value method** = a method with a value receiver: `func (c Counter) Show()`.

- It is in the method set of `Counter`.
- It is also in the method set of `*Counter`.

### Pointer Methods

**Pointer method** = a method with a pointer receiver: `func (c *Counter) Inc()`.

- It is in the method set of `*Counter` **only**.
- Not in the method set of `Counter`.

### Method Availability Rules

| Type | Value methods `(t T)` | Pointer methods `(t *T)` |
|---|---|---|
| `T` | ✓ | ✗ |
| `*T` | ✓ | ✓ |

- When **calling** on a variable, Go helps: `c.Inc()` becomes `(&c).Inc()` automatically.
- When **fitting an interface**, Go does not help: a `T` value does not have the pointer methods.

```go runnable title="Calling both kinds on a variable"
package main

import "fmt"

type Counter int

func (c *Counter) Inc() {
	*c++
}

func (c Counter) Show() string {
	return fmt.Sprint("count = ", int(c))
}

func main() {
	var visitors Counter
	visitors.Inc()
	visitors.Inc()
	fmt.Println(visitors.Show())
}
```

```text
count = 2
```

- `visitors` is a `Counter`, not a pointer.
- `visitors.Inc()` still works, because Go takes its address for you.

Now an interface:

```go
type Incrementer interface {
	Inc()
}

var c Counter
var i Incrementer = c   // error: Counter does not implement Incrementer
                        //        (method Inc has pointer receiver)
var j Incrementer = &c  // OK: *Counter has Inc
```

- Store a **pointer** in the interface when its methods have pointer receivers.

In this lab, the runner does not report the "pointer receiver" error above.

The real `go` tool does. That is why it is shown as a plain block. Always use `&value` when an interface needs pointer methods.

**Simple lesson:** `*T` has every method, `T` has only the value methods, and interfaces follow these rules strictly.

## Embedded Interfaces

### Interface Composition

**Interface composition** = building an interface by listing other interfaces inside it.

```go runnable title="Reader + Writer = ReadWriter"
package main

import "fmt"

type Reader interface {
	Read() string
}

type Writer interface {
	Write(s string)
}

type ReadWriter interface {
	Reader
	Writer
}

type Notebook struct {
	pages []string
}

func (n *Notebook) Write(s string) {
	n.pages = append(n.pages, s)
}

func (n *Notebook) Read() string {
	return fmt.Sprint(n.pages)
}

func main() {
	var rw ReadWriter = &Notebook{}
	rw.Write("Maths homework")
	rw.Write("Science project")
	fmt.Println(rw.Read())
}
```

```text
[Maths homework Science project]
```

- `ReadWriter` needs every method of `Reader` and `Writer`.
- `*Notebook` has both, so it fits all three interfaces.
- Go's own `io.ReadWriter` is built exactly like this.

### Reusable Contracts

**Contract** = an interface seen as a promise: "this type can do these things".

- Small contracts: `Reader`, `Writer`, `Closer`.
- Combine them as needed: `ReadWriter`, `ReadCloser`, `ReadWriteCloser`.
- A function asks for **only** what it needs.

```go runnable title="Ask only for what you need"
package main

import "fmt"

type Reader interface {
	Read() string
}

type Notice struct {
	Text string
}

func (n Notice) Read() string {
	return n.Text
}

func announce(r Reader) {
	fmt.Println("Announcement:", r.Read())
}

func main() {
	announce(Notice{"Sports day on Friday"})
}
```

```text
Announcement: Sports day on Friday
```

- `announce` only reads, so it asks for a `Reader`, not a `ReadWriter`.
- More types can then use it.

**Easy way to remember**

- `type X Y` = new type, new meaning, can have methods.
- `type X = Y` = same type, new name.
- Pointer methods → put a pointer in the interface.
- Small interfaces, combined when needed.

**Simple lesson:** build big interfaces from small ones, and let functions ask for the smallest one they need.

## TL;DR

1. **Type definition** = `type Celsius float64`. A new type with its own meaning.
   - Go will not mix it with other types without conversion.
   - Can have methods, even on numbers and slices.
2. **Type alias** = `type RollNo = int`. Same type, another name.
3. **Method set** = the methods a type has.
   - `T` has value methods. `*T` has value and pointer methods.
   - Calls on variables get automatic `&`. Interfaces do not.
4. **Embedded interfaces** = `type ReadWriter interface { Reader; Writer }`.
   - Ask for the smallest interface you need.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Adding `Celsius` and `Kilograms` | Both are float64 underneath | They are different types | `mismatched types` |
| `type RollNo = int` then adding a method | Aliases are new types | An alias of `int` is `int` | `cannot define new methods on non-local type` |
| Putting a value in an interface that needs pointer methods | Go adds `&` for you | Only for direct calls | `method has pointer receiver` |
| Using a plain `string` for grades everywhere | Strings are fine | A `Grade` type documents and checks | Typos like `"a"` slip through. |
| Giant interfaces | One big interface is simpler | Small ones combine better | Few types can fit it. |
| Methods on a type from another package | You can extend any type | Only types defined in your package | A build error. Define your own type. |

## Check Yourself

## Exercises

## What to Remember

- `type X Y` makes a new type with its own meaning. Go will not mix it up.
- `type X = Y` is only another name for the same type.
- Any type defined in your package can have methods.
- `*T` has all methods. `T` has only value methods. Interfaces follow this strictly.
- Build big interfaces from small ones, and accept the smallest you need.

Next: **Generics**. You will learn how to write one function or type that works with many types, safely.
