I need to differentiate such types as
type A []byte
from a []byte
. Using reflect
, reflect.TypeOf(A{}).Kind
tells me that it is a Slice
of byte
. How can I differentiate []byte{}
from A{}
, without having a bounded list of types to check for?
Are there new ways to do it in newer versions of Go?
Some background
First let's clear some things related to types. Quoting from Spec: Types:
So there are (predeclared) named types such as
string
,int
etc, and you may also create new named types using type declarations (which involves thetype
keyword) such astype MyInt int
. And there are unnamed types which are the result of a type literal (applied to / including named or unnamed types) such as[]int
,struct{i int}
,*int
etc.You can get the name of a named type using the
Type.Name()
method, which "returns an empty string for unnamed types":There are types which are predeclared and are ready for you to use them (either as-is, or in type literals):
Predeclared types are:
You may use
Type.PkgPath()
to get a named type's package path, which "if the type was predeclared (string
,error
) or unnamed (*T
,struct{}
,[]int
), the package path will be the empty string":So you have 2 tools available to you:
Type.Name()
to tell if the type is a named type, andType.PkgPath()
to tell if the type is not predeclared and is a named type.But care must be taken. If you use your own, named type in a type literal to construct a new type (e.g.
[]A
), that will be an unnamed type (if you don't use thetype
keyword to construct a new, named type):What can you do in such cases? You may use
Type.Elem()
to get the type's element type, if type'sKind
isArray
,Chan
,Map
,Ptr
, orSlice
(elseType.Elem()
panics):Summary
Type.PkgPath()
can be used to "filter out" predeclared and unnamed types. IfPkgPath()
returns a non-empty string, you can be sure it's a "custom" type. If it returns an empty string, it still may be an unnamed type (in which caseType.Name()
returns""
) constructed from a "custom" type; for that you may useType.Elem()
to see if it is constructed from a "custom" type, which may have to be applied recursively:Try all the examples on the Go Playground.
Special case #1: Anonymous struct types
There is also the case of an anonymous struct type which is unnamed, but it may have a field of a "custom" type. This case can be handled by iterating over the fields of the struct type and performing the same check on each field, and if any of them is found to be a "custom" type, we can claim the whole struct type to be "custom".
Special case #2: Map types
In case of maps we may consider an unnamed map type "custom" if any of its key or value type is "custom".
The value type of a map can be queried with the above mentioned
Type.Elem()
method, and the key type of a map can be queried with theType.Key()
method - we also have to check this in case of maps.Example implementation
Testing it (try it on the Go Playground):