x.(T): getting the value out of an interface
An interface value hides the concrete type it holds. A type assertion x.(T) asks for it back.
Output:
gopher 6
0 false
string of length 6
x must have an interface type. Asserting on a concrete type fails to compile: invalid operation: s (variable of type string) is not an interface.
The one-value form panics
When the assertion is wrong, the single-value form panics with a message naming both types:
Output:
recovered: interface conversion: interface {} is int, not string
Use the one-value form only when a different type would be a programming error you want to crash on. Everywhere else, use comma-ok. On failure, comma-ok gives the zero value of T and false.
Asserting on a nil interface fails too: var v any; v.(string) panics with interface conversion: interface {} is nil, not string, and the comma-ok form returns "", false.
Asserting to another interface
T can be an interface type. The assertion then succeeds if the dynamic value implements T, and the result keeps the same dynamic value. This is how the standard library checks for optional capabilities.
io.Copy checks whether its source implements io.WriterTo and uses it when it does. fmt checks for Stringer and error. The pattern lets a small interface stay small while callers still benefit from richer implementations.
Type switch
When a value could be one of several types, a type switch replaces a chain of assertions. x.(type) is only valid inside a switch.
Output:
hi
integer 7
integer 8
3.14
nil
error: boom
unhandled []int
Rules worth knowing:
- In a case with one type,
xhas that type. In a case listing several types, and indefault,xhas the type of the original interface (anyhere). - Cases are checked in order. Put more specific interfaces before broader ones, since a value can satisfy several.
case nilmatches only a nil interface, not an interface holding a nil pointer.- There is no
fallthroughin a type switch.
errors.As: the assertion for wrapped errors
Errors are often wrapped with context: fmt.Errorf("load config: %w", err). A direct type assertion looks only at the outermost error and misses the one inside. errors.As walks the chain.
Output:
type assertion finds it: false
errors.As finds it: open /no/such/file
Use a type assertion on errors only when you know the error is not wrapped, which in practice means almost never. The custom errors page covers errors.As, errors.Is and defining your own error types.
Assertions vs conversions vs generics
| You have | You want | Use |
|---|---|---|
int | float64 | conversion: float64(n) |
any holding an int | the int | assertion: v.(int) |
any of several possible types | branch on type | type switch |
an error that may be wrapped | a specific error type | errors.As |
| a function that works for many types | compile-time safety | generics |
Code full of any and type switches is often a sign that generics or a proper interface would express the intent better and catch mistakes at compile time.
Common mistakes
- Using the panicking form on untrusted data. Decoded JSON, map values of type
any, plugin inputs: always comma-ok. - Asserting to the wrong numeric type. JSON numbers decode into
anyasfloat64, sov.(int)fails on them. - Asserting a value type when a pointer is stored. If the interface holds
*User, thenv.(User)fails. Assertv.(*User). - Type assertions on errors. Use
errors.As.
Frequently Asked Questions
What is a type assertion in Go?
An expression x.(T) where x is an interface value. If T is a concrete type, it returns the value stored in x as a T. If T is an interface type, it checks that the stored value also implements T. The one-value form panics when the check fails; the two-value form v, ok := x.(T) reports it in ok.
How do I check the type of an interface value in Go?
Use a type switch: switch v := x.(type) { case int: ...; case string: ...; default: ... }. Inside each case v has that case's type. For a single type, the comma-ok assertion s, ok := x.(string) is shorter. fmt.Printf("%T", x) prints the type name for debugging.
What is the difference between a type assertion and a type conversion in Go?
A conversion T(x) turns a value of one type into another, like float64(n); the compiler decides whether it is allowed, and it cannot fail at run time. An assertion x.(T) works only on interface values and checks at run time what type is stored inside. You cannot write int(x) for an any holding an int; you need x.(int).
Should I use a type assertion to check error types?
No. Use errors.As(err, &target). A direct assertion err.(*MyError) only looks at the outer error, so it fails as soon as the error is wrapped with fmt.Errorf("...: %w", err). errors.As walks the wrap chain.