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):umust be an interface type, or it panics.AssignableToandConvertibleTo.Comparable(), which tells you whether==on values of that type would panic.NumMethod()andMethod(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
- Q109How do you write a generic function that needs to call a pointer-receiver method on
T? (thePT interface{ *T; M() }pattern) - Q110How do you "seal" an interface so other packages cannot implement it? Can the seal be bypassed?