Menu

Golang Type Assertion: x.(T), Comma-ok and Type Switch

A type assertion gets the concrete value out of an interface. Learn x.(T), the comma-ok form that never panics, asserting to another interface, type switches, and errors.As for wrapped errors.

This page includes runnable editors - edit, run, and see output instantly.

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, x has that type. In a case listing several types, and in default, x has the type of the original interface (any here).
  • Cases are checked in order. Put more specific interfaces before broader ones, since a value can satisfy several.
  • case nil matches only a nil interface, not an interface holding a nil pointer.
  • There is no fallthrough in 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 haveYou wantUse
intfloat64conversion: float64(n)
any holding an intthe intassertion: v.(int)
any of several possible typesbranch on typetype switch
an error that may be wrappeda specific error typeerrors.As
a function that works for many typescompile-time safetygenerics

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 any as float64, so v.(int) fails on them.
  • Asserting a value type when a pointer is stored. If the interface holds *User, then v.(User) fails. Assert v.(*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.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED