Type Assertions and Type Switches
An interface value hides its concrete type. Sometimes you need it back: to use a method the interface doesn't list, to handle a few types differently, or to make sense of an any. A type assertion extracts one specific type; a type switch branches on several. This lesson covers both, plus checking for optional behaviour with interface assertions.
Type assertions
x.(T) asserts that the interface value x holds a T and returns it. Use the two-value "comma ok" form to test safely; the single-value form panics if the type is wrong:
package main import "fmt" func main() { var v any = "hello" s, ok := v.(string) fmt.Printf("%q %v\n", s, ok) n, ok := v.(int) fmt.Println(n, ok) // zero value and false: no panic fmt.Println(v.(string) + "!") // fine: it is a string fmt.Println(v.(int)) // panics }
"hello" true
0 false
hello!
panic: interface conversion: interface {} is string, not int
goroutine 1 [running]:
…
exit status 2Use the single-value form only when a wrong type would be a bug in your own code. For anything that depends on input, use comma-ok.
Type switches
A type switch tests an interface value against several types in turn. In each case, the variable declared in the switch has that case's type:
package main import ( "fmt" "strings" ) func describe(v any) string { switch x := v.(type) { case nil: return "nothing" case int: return fmt.Sprintf("int, doubled: %d", x*2) case string: return fmt.Sprintf("string of length %d: %s", len(x), strings.ToUpper(x)) case bool: if x { return "yes" } return "no" case []int: return fmt.Sprintf("slice with %d ints", len(x)) case float32, float64: return fmt.Sprintf("a float: %v", x) // several types: x stays an any default: return fmt.Sprintf("something else (%T)", x) } } func main() { for _, v := range []any{42, "go", true, []int{1, 2}, 2.5, nil, map[string]int{}} { fmt.Println(describe(v)) } }
int, doubled: 84 string of length 2: GO yes slice with 2 ints a float: 2.5 nothing something else (map[string]int)
When a case lists several types, the variable keeps the interface type, because Go can't know which of them it is.
Checking for optional behaviour
You can also assert to an interface type to ask "does this value have this method?" Standard library code does this to use a faster path when it's available. Here a report prints any item, and adds a summary for items that can provide one:
package main import "fmt" type Summarizer interface { Summary() string } type Invoice struct { ID int Total float64 } func (i Invoice) Summary() string { return fmt.Sprintf("invoice #%d, %.2f due", i.ID, i.Total) } type Note string func report(items []any) { for _, it := range items { if s, ok := it.(Summarizer); ok { fmt.Println("-", s.Summary()) } else { fmt.Println("-", it) } } } func main() { report([]any{Invoice{17, 99.5}, Note("call the bank"), 42}) }
- invoice #17, 99.50 due - call the bank - 42
Errors and type switches
Type switches are useful on errors too, though for wrapped errors errors.As and errors.AsType (lesson 45) are usually better, because they also look inside wrapped errors:
package main import ( "fmt" "os" "strconv" ) func explain(err error) string { switch e := err.(type) { case nil: return "ok" case *strconv.NumError: return "bad number: " + e.Num case *os.PathError: return "file problem with " + e.Path default: return "other error: " + e.Error() } } func main() { _, err1 := strconv.Atoi("12x") _, err2 := os.Open("nope.txt") fmt.Println(explain(err1)) fmt.Println(explain(err2)) fmt.Println(explain(nil)) }
bad number: 12x file problem with nope.txt ok
When to use them
Type switches are the right tool when the set of types is genuinely open — values decoded from JSON, an any from a library — or when checking for optional methods. If you find yourself switching on your own types to decide what to do, consider adding a method to an interface instead, so each type carries its own behaviour. And if you're using any just to accept several types of one kind, generics (lesson 47) give you type safety back.
Try it yourself
JSON decoded into any produces map[string]any, []any, string, float64, bool and nil. Write a function that counts how many numbers appear anywhere inside such a value.
3