Type Alias vs Type Definition in Go: when to use what

leangaurav
5 min readSep 11, 2023

Let’s start coding !!

Look at the code below and think what it prints. [%T is used to print type info]

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func main() {
var i1 int1
var i2 int2
fmt.Printf("%T, %T", i1, i2)
}

Output

int, main.int2

source

Interestingly, i1 prints int and i2 prints main.int2.
Why we see this output ?

Think !!

i1 is an alias. Alias is a second name for an exising type and it’s just like replacing all occurences of int1 with int.
Whereas int2 is a type definition. Notice the output says: “main.int2”. What this means is int2 is a new type.

What’s the difference ?

We need to look at it from following angles:

  1. Behavior of operations wrt the new type

We want to test if the new types behave exactly like the original type int. To do this, we try some operations that are supported by int type.

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func main() {
var i1 int1
var i2 int2

// Check if integer operations works
i1 += 10
i2 += 10

i1 -= 5
i2 -= 5

// check if the new type needs an explicit type conversion
var i3 int = i1
var i4 int = i2

fmt.Println(i1, i2, i3, i4)
}

Output

./prog.go:21:15: cannot use i2 (variable of type int2) as int value in variable declaration

Go build failed.

source

This is expected based on the output of the first program. The newly defined type cannot be assigned to an int var since i2's type is not int, it’s main.int2 instead. Hence we need an explicit type cast like below and everything would just work fine.

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func main() {
var i1 int1
var i2 int2

// Check if integer operations works
i1 += 10
i2 += 10

i1 -= 5
i2 -= 5

// check if the new type needs an explicit type conversion
var i3 int = i1
var i4 int = int(i2) // NOTICE HERE !!

fmt.Println(i1, i2, i3, i4)
}

Output

5 5 5 5

2. Defining new operations/methods

Let’s try to define a method on our newly defined type.

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func (i int1) ToString() string {
return fmt.Sprintf("%d", i)
}

func (i int2) ToString() string {
return fmt.Sprintf("%d", i)
}

func main() {
}

Output

./prog.go:8:7: cannot define new methods on non-local type int

source

Again similar to previous example we, the code doesn’t run and we get an error. But this time the error is for the type alias instead of the new type defined using type definition.

Since this operation itself doesn’t work, we remove the method func (i int1) ToString() string and try to run the remaining code.

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func (i int2) ToString() string {
return fmt.Sprintf("%d", i)
}

func main() {
var i1 int1
var i2 int2

fmt.Println(i1, i2.ToString())
}

Output

0 0

3. Assignment operations

We already saw one error in our second example, where a newly defined type could not be assigned to the original underlying type. Let’s look at the other side now.

package main

import "fmt"

type int1 = int // OPTION-1
type int2 int // OPTION-2

func main() {
var i1 int1
var i2 int2

const (
c1 = 5 // untyped numeric const
c2 int = 10 // typed numeric const
)

// assign untyped constant
i1 = c1
i2 = c1

// assign a typed constant
i1 = c2
i2 = c2 // LINE-23

// assign a typed var
var i int = 15
i1 = i
i2 = i // LINE-28

fmt.Println(i1, i2)
}

Output

./prog.go:23:7: cannot use c2 (constant 10 of type int) as int2 value in assignment
./prog.go:28:7: cannot use i (variable of type int) as int2 value in assignment

Go build failed.

source

The error messages are pretty much self explanatory.

However you must notice that i2 = c2 fails while i2 = c1 doesn’t. That’s because go does a strict type matching. So for typed constants(c2) or for typed variables (line 28), type must match whereas for untyped constants (c1), type can be inferred within the expression if it’s compatible.

Applications

Type Alias

It can simply be used to define another name (alias 😝) which can be used in place of the orignal name. When would you do that ?
When you don’t like the original type name, maybe too vebose🙈.
There can actually be valid reasons for doing this, see the “reading material” at the end.

or maybe you want to tell the world that you have written this code 🤣

package main

import "fmt"

type gaurav = int

func main() {
var leangaurav gaurav
leangaurav++
fmt.Println(leangaurav)
}
https://go.dev/play/p/Xl1y-uXiGjd

Type Definition

There might be other reasons for using type definition, but one of them is using type defintion for enum like behavior. Like this example from go spec which is slighly modified for ease of understanding. Go through the spec to read the original code.

type TimeZone int

const (
EST TimeZone = -5
CST TimeZone = -6
MST TimeZone = -7
PST TimeZone = -8
)

func (tz TimeZone) String() string {
return fmt.Sprintf("GMT%+dh", tz)
}

More reading material about type alias and type definition

  1. https://go.dev/talks/2016/refactor.article
that’s all folks!

Try to play with the examples yourself as there’s much more to explore.

You can find me on Linkedin 👋🏻👋🏻.

--

--

leangaurav

Engineer | Trainer | writes about Practical Software Engineering | Find me on linkedin.com/in/leangaurav | Discuss anything topmate.io/leangaurav