Go's
reflect.TypeFor()
is a generic function that returns the
reflect.Type
for its type argument. It was added in Go 1.22, and its initial implementation was quite simple but still valuable , because it encapsulated a complicated bit of
reflect
usage. Here is that implementation:
func TypeFor[T any]() Type {
return TypeOf((*T)(nil)).Elem()
}
How this works is that it constructs a nil pointer value of the type 'pointer to T', gets the
reflect.Type
of that pointer, and then uses Type.Elem() to go from the pointer's Type to the Type for T itself. This requires constructing and using this 'pointer to T' type (and its
reflect.Type
) even though we only what the
reflect.Type
of T itself. All of this is necessary for reasons to do with interface types .
Recently,
reflect.TypeFor()
was optimized a bit, in CL 555597, "optimize TypeFor for non-interface types" . The code for this optimization is a bit tricky and I had to stare at it for a while to understand what it was doing and how it worked. Here is the new version, which starts with the new optimization and ends with the old code:
func TypeFor[T any]() Type {
var v T
if t := TypeOf(v); t != nil {
return t
}
return TypeOf((*T)(nil)).Elem()
}
What this does is optimize for the case where you're using TypeFor() on a non-interface type, for example '
reflect.TypeFor[int64]()
' (although you're more likely to use this with more complex things like struct types). When T is a non-interface type, we don't need to construct a pointer to a value of the type; we can directly obtain the Type from
reflect.TypeOf
. But how do we tell whether or not T is an interface type? The answer turns out to be right there in the documentation for
reflect.TypeOf
:
[...] If [TypeOf's argument] is a nil interface value, TypeOf returns nil.
So what the new code does is construct a zero value of type T, pass it to TypeOf(), and check what it gets back. If type T is an interface type, its zero value is a nil interface and TypeOf() will return nil; otherwise, the return value is the
reflect.Type
of the non-interface type T.
The reason that
reflect.TypeOf
returns nil for a nil interface value is because it has to. In Go,
nil
is only sort of typed , so if a nil interface value is passed to TypeOf(), there is effectively no type information available for it; its old interface type is lost when it was converted to '
any
', also known as the empty interface . So all TypeOf() can return for such a value is the nil result of 'this effectively has no useful type information'.
Incidentally, the TypeFor() code is also another illustration of how in Go, interfaces create a difference between two sorts of nils . Consider calling '
reflect.TypeFor[*os.File]()
'. Since this is a pointer type, the zero value '
v
' in TypeFor() is a nil pointer. But
os.File
isn't an interface type, so TypeOf() won't be passed a nil interface and can return a Type, even though the underlying value in the interface that TypeOf() receives is a nil pointer.