Go

How do you check at runtime whether a type implements an interface using reflection? Why is reflect.TypeOf awkward for interface types?

Question 111HardGo 1.22 to 1.25
// Go 1.22+: reflect.TypeFor works for interface types directly
writerT := reflect.TypeFor[io.Writer]()

fmt.Println(reflect.TypeFor[*bytes.Buffer]().Implements(writerT)) // true
fmt.Println(reflect.TypeFor[bytes.Buffer]().Implements(writerT))  // false: Write has a pointer receiver

// Pre-1.22 idiom
var w io.Writer
fmt.Println(reflect.TypeOf(w))                          // <nil>
fmt.Println(reflect.TypeOf((*io.Writer)(nil)).Elem())   // io.Writer

// Without reflection, for a value you already hold:
var x any = &bytes.Buffer{}
_, ok := x.(io.Writer) // true

reflect.TypeOf takes an any. Passing an interface value converts it to any, which keeps only the dynamic type. A nil io.Writer has no dynamic type, so you get nil, and a non-nil one gives you the concrete type, never io.Writer itself. The old workaround takes the type of a pointer to the interface, which is a concrete type, and calls Elem(). Go 1.22's generic reflect.TypeFor[T]() removes the need for that trick.

Related methods on reflect.Type:

  • Implements(u): u must be an interface type, or it panics.
  • AssignableTo and ConvertibleTo.
  • Comparable(), which tells you whether == on values of that type would panic.
  • NumMethod() and Method(i), which list only exported methods for concrete types.

This is how encoding/json decides whether to call MarshalJSON (it also checks reflect.PointerTo(t).Implements for addressable values), and how DI containers and plugin loaders validate types.

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions