Go Type Assertion

Type assertion extracts the concrete value stored inside an interface variable. When a value is stored as an interface, its specific type information is still there but hidden. Type assertion reveals and retrieves it.

Basic Type Assertion

package main

import "fmt"

func main() {
    var i interface{} = "Hello, Go!"

    s := i.(string) // assert that i holds a string
    fmt.Println(s)  // Hello, Go!
}

Type Assertion Syntax

value := interfaceVariable.(ConcreteType)

                │                  │
                │                  └── the type being asserted
                └──────────────────── the interface variable

If the assertion is correct → returns the concrete value
If the assertion is wrong  → panics at runtime

Safe Type Assertion (Two-Value Form)

The safe form returns two values: the concrete value and a boolean indicating success. This prevents a panic if the assertion is wrong.

package main

import "fmt"

func main() {
    var i interface{} = 42

    s, ok := i.(string) // try to assert as string
    fmt.Println(s, ok)  // "" false — i holds an int, not string

    n, ok2 := i.(int)   // try to assert as int
    fmt.Println(n, ok2) // 42 true
}

Type Assertion vs Type Conversion

FeatureType AssertionType Conversion
PurposeExtract concrete type from interfaceConvert one type to another
Syntaxx.(T)T(x)
Works onInterface variables onlyCompatible concrete types
Panic riskYes — use two-value formNo (compile-time checked)

Type Switch

A type switch checks an interface variable against multiple types in sequence — a clean alternative to chaining type assertions.

package main

import "fmt"

func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %t\n", v)
    case []int:
        fmt.Printf("Int slice: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    describe(42)
    describe("Go")
    describe(true)
    describe([]int{1, 2, 3})
}

Output:

Integer: 42
String: Go
Boolean: true
Int slice: [1 2 3]

Practical Use – Interface Conversion in a Function

package main

import "fmt"

type Animal interface {
    Sound() string
}

type Dog struct{ Name string }
type Cat struct{ Name string }

func (d Dog) Sound() string { return "Woof" }
func (c Cat) Sound() string { return "Meow" }

func greet(a Animal) {
    if dog, ok := a.(Dog); ok {
        fmt.Printf("Good dog, %s! You say: %s\n", dog.Name, dog.Sound())
    } else if cat, ok := a.(Cat); ok {
        fmt.Printf("Nice cat, %s! You say: %s\n", cat.Name, cat.Sound())
    }
}

func main() {
    greet(Dog{Name: "Bruno"})
    greet(Cat{Name: "Whiskers"})
}

Output:

Good dog, Bruno! You say: Woof
Nice cat, Whiskers! You say: Meow

Key Points

  • Type assertion extracts the concrete value stored inside an interface
  • The single-value form x.(T) panics if the assertion fails
  • The two-value form v, ok := x.(T) is safe — check ok before using v
  • A type switch handles multiple possible types cleanly without chained assertions

Leave a Comment