How to find the type of an object in Go?
Categories:
How to Determine the Type of an Object in Go

Learn various methods to identify the type of a variable or interface in Go, leveraging built-in features and the reflect
package for dynamic type inspection.
Understanding the type of a variable is fundamental in any programming language. In Go, a statically typed language, types are usually known at compile time. However, there are scenarios, especially when working with interfaces or dynamic data, where you need to determine an object's type at runtime. Go provides several mechanisms to achieve this, ranging from simple type assertions to the powerful reflect
package.
Basic Type Checking: Type Assertions and Type Switches
For variables whose underlying type is known to be one of a few possibilities, Go's type assertion and type switch mechanisms are the most straightforward and idiomatic ways to determine the type. These methods are compile-time safe and efficient.
package main
import (
"fmt"
)
func describe(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}
func main() {
var i interface{} = "hello"
s, ok := i.(string)
fmt.Println(s, ok) // Output: hello true
f, ok := i.(float64)
fmt.Println(f, ok) // Output: 0 false
i = 42
switchedType(i)
i = "world"
switchedType(i)
i = true
switchedType(i)
}
func switchedType(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
Using type assertions and type switches for basic type checking.
%T
format verb in fmt.Printf
is a quick way to print the type of a variable. While useful for debugging, it's not suitable for programmatic type checking or control flow.Dynamic Type Inspection with the reflect
Package
When you need to inspect types dynamically, especially when dealing with arbitrary data structures or when the possible types are not known in advance, Go's reflect
package is indispensable. The reflect
package provides functions to inspect the type and value of any variable at runtime. It's particularly useful for serialization, ORMs, and generic programming.
flowchart TD A[Input: interface{}] --> B{reflect.TypeOf(i)} B --> C{reflect.ValueOf(i)} C --> D{Value.Kind()} C --> E{Value.Type()} D --> F[Basic Type (e.g., int, string, struct)] E --> G[Full Type (e.g., *main.MyStruct, []int)] F --> H{Perform actions based on Kind} G --> I{Perform actions based on Type} H --> J[Output: Dynamic Behavior] I --> J
Flowchart illustrating the use of reflect.TypeOf
and reflect.ValueOf
.
The reflect
package offers two main types for introspection: reflect.Type
and reflect.Value
.
reflect.TypeOf(i interface{}) Type
: Returns thereflect.Type
that represents the dynamic type of the interface valuei
. This type describes the static type of the variable.reflect.ValueOf(i interface{}) Value
: Returns thereflect.Value
that represents the dynamic value of the interface valuei
. This value allows manipulation of the underlying data.
From these, you can extract information like the Kind
(the underlying type category, e.g., int
, string
, struct
, ptr
, slice
) and the Name
(the type's name within its package).
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
Name string
Age int
}
func inspectType(i interface{}) {
t := reflect.TypeOf(i)
v := reflect.ValueOf(i)
fmt.Printf("\nInspecting value: %v\n", i)
fmt.Printf(" Type: %v\n", t.Name())
fmt.Printf(" Kind: %v\n", t.Kind())
if t.Kind() == reflect.Struct {
fmt.Printf(" Number of fields: %d\n", t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf(" Field %d: %s (Type: %v, Tag: %v)\n", i, field.Name, field.Type, field.Tag)
}
}
if t.Kind() == reflect.Ptr {
fmt.Printf(" Points to Kind: %v\n", t.Elem().Kind())
fmt.Printf(" Points to Type: %v\n", t.Elem().Name())
// To get the value pointed to by a pointer, use Elem() on the Value as well
if !v.IsNil() {
fmt.Printf(" Value pointed to: %v\n", v.Elem())
}
}
}
func main() {
inspectType(123)
inspectType("hello Go")
inspectType(3.14)
inspectType(true)
myS := MyStruct{Name: "Alice", Age: 30}
inspectType(myS)
inspectType(&myS)
var myNilPtr *MyStruct
inspectType(myNilPtr)
slice := []int{1, 2, 3}
inspectType(slice)
}
Using reflect.TypeOf
and reflect.ValueOf
for detailed type inspection.
reflect
package is powerful but comes with a performance overhead. Use it judiciously, primarily when static type checking is insufficient. Over-reliance on reflection can also make code harder to read and maintain.Comparing Types
Once you have a reflect.Type
object, you can compare it with other reflect.Type
objects or with predefined types. This is useful for ensuring that a dynamic type matches an expected type.
package main
import (
"fmt"
"reflect"
)
func main() {
var myInt int
var myString string
var myFloat float64
// Get reflect.Type for built-in types
intType := reflect.TypeOf(myInt)
stringType := reflect.TypeOf(myString)
floatType := reflect.TypeOf(myFloat)
// Example values
var i interface{} = 42
var s interface{} = "GoLang"
// Get reflect.Type of interface values
valIntType := reflect.TypeOf(i)
valStringType := reflect.TypeOf(s)
fmt.Printf("Is 42 an int? %t\n", valIntType == intType)
fmt.Printf("Is \"GoLang\" a string? %t\n", valStringType == stringType)
fmt.Printf("Is 42 a float64? %t\n", valIntType == floatType)
// Comparing Kinds
fmt.Printf("Kind of 42 is int? %t\n", valIntType.Kind() == reflect.Int)
fmt.Printf("Kind of \"GoLang\" is string? %t\n", valStringType.Kind() == reflect.String)
}
Comparing reflect.Type
objects and reflect.Kind
values.