Skip to content

Type Assertions

When a value is stored in an interface variable, Go keeps two things under the hood: a pointer to the concrete type descriptor and a pointer to the concrete value. A type assertion extracts the concrete value back out.

var s Shape = Circle{Radius: 5}
c := s.(Circle) // asserts that s holds a Circle
fmt.Println(c.Radius)

If s does not hold a Circle, this panics at runtime. Use this form only when you are certain of the dynamic type — for example, right after a type check.

The safe variant returns a boolean instead of panicking:

c, ok := s.(Circle)
if ok {
fmt.Println("it's a circle with radius", c.Radius)
} else {
fmt.Println("not a circle")
}

ok is true when the assertion succeeds, false otherwise. c is the zero value of Circle when ok is false. Always prefer the comma-ok form when the dynamic type is uncertain.

When you need to branch on several possible types, a type switch is cleaner than chained if ok blocks:

switch v := x.(type) {
case Circle:
fmt.Println("circle", v.Radius)
case Rect:
fmt.Println("rect", v.W, v.H)
default:
fmt.Printf("unknown: %T\n", v)
}

Inside each case, v is already the concrete type — no additional assertion needed. The default case fires when none of the listed types match.

You can also assert from one interface to another:

var w io.Writer = os.Stdout
if rc, ok := w.(io.ReadWriter); ok {
// os.Stdout implements io.ReadWriter
_ = rc
}

This is how standard library code checks for optional capabilities (e.g., whether a Writer also supports Seek).

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 identify(a Animal) {
// comma-ok form — safe
if d, ok := a.(Dog); ok {
fmt.Printf("Dog named %s says %s\n", d.Name, d.Sound())
return
}
// type switch for multiple cases
switch v := a.(type) {
case Cat:
fmt.Printf("Cat named %s says %s\n", v.Name, v.Sound())
default:
fmt.Printf("Unknown animal: %T\n", v)
}
}
func main() {
animals := []Animal{Dog{Name: "Rex"}, Cat{Name: "Luna"}}
for _, a := range animals {
identify(a)
}
}
What does `v, ok := x.(T)` do when x does NOT hold a value of type T?
Inside a type-switch case `case Rect:`, what is the type of the switched variable `v`?
Which form should you use when you are NOT certain of the dynamic type?
What does `%T` print in a format string?