Golang - Interfaces - Type Assertions and Type Switches

Publish date: 2025-09-25
Tags: <a href="https://programmercave.com/tags/Go/">Go</a>

Introduction

When you have a value stored in an interface variable, the compiler only knows about the methods defined by the interface. To access the underlying concrete type or its specific fields and methods, you need a way to get it back. This is done with type assertions and type switches.


1. Type Assertion

A type assertion is an operation that checks the concrete type of an interface value and extracts it.

Analogy: Think of an interface as a wrapped gift. You know it’s a “gift” (the interface), but you don’t know if it’s a “book” or a “toy” (the concrete types) inside. A type assertion is like unwrapping the gift to see what’s really there.

Syntax

There are two forms of type assertion.

a) The Unsafe, Panicking Form

value := i.(T)

This form should only be used when you are absolutely certain of the type and a wrong type would be an unrecoverable programming error.

var i any = "hello"
s := i.(string) // This works
fmt.Println(s)

// n := i.(int) // This would PANIC!

b) The “Comma, Ok” Idiom (Safe Form)

This is the idiomatic and preferred way to do a type assertion.

value, ok := i.(T)
package main

import "fmt"

func main() {
	var i any = "hello"

	// Safely assert that `i` is a string
	s, ok := i.(string)
	if ok {
		fmt.Printf("It's a string with value: '%s'\n", s)
	} else {
		fmt.Println("It's not a string.")
	}

	// Safely assert that `i` is an int
	n, ok := i.(int)
	if ok {
		fmt.Printf("It's an int with value: %d\n", n)
	} else {
		fmt.Println("It's not an int.")
	}
}

Output:

It's a string with value: 'hello'
It's not an int.

2. Type Switch

A type assertion is great if you only need to check for one specific type. If you need to handle several different possible types, a type switch is a much cleaner and more powerful tool.

A type switch is like a regular switch statement, but it switches on the type of the interface variable.

Syntax

switch v := i.(type) {
case T1:
    // Code to run if i holds type T1
    // `v` has type T1 here
case T2:
    // Code to run if i holds type T2
    // `v` has type T2 here
default:
    // Code to run if i holds none of the above types
    // `v` has the same type as `i` (the interface type)
}

Example:

package main

import "fmt"

func do(i any) {
	switch v := i.(type) {
	case int:
		fmt.Printf("It's an int, doubled is %d\n", v*2)
	case string:
		fmt.Printf("It's a string, its length is %d\n", len(v))
	case bool:
		fmt.Printf("It's a boolean: %t\n", v)
	default:
		fmt.Printf("I don't know the type, but the value is %v\n", v)
	}
}

func main() {
	do(21)      // It's an int, doubled is 42
	do("hello") // It's a string, its length is 5
	do(true)    // It's a boolean: true
	do(3.14)    // I don't know the type, but the value is 3.14
}

Key Benefits of a Type Switch:

Tags: <a href="https://programmercave.com/tags/Go/">Go</a>