Use the built-in fmt.Sprintf("%T", variable) function or the reflect package to inspect a variable's type at runtime. For simple cases, fmt.Sprintf is the quickest approach, while reflect.TypeOf provides more detailed type information for complex scenarios.
Here is a practical example using fmt.Sprintf for quick debugging:
package main
import (
"fmt"
)
func main() {
var x int = 42
var y string = "hello"
var z interface{} = 3.14
fmt.Printf("Type of x: %T\n", x) // Output: Type of x: int
fmt.Printf("Type of y: %T\n", y) // Output: Type of y: string
fmt.Printf("Type of z: %T\n", z) // Output: Type of z: float64
}
If you need to perform logic based on the type or inspect dynamic values stored in interface{}, use the reflect package:
package main
import (
"fmt"
"reflect"
)
func checkType(v interface{}) {
t := reflect.TypeOf(v)
fmt.Printf("Kind: %v, Type: %v\n", t.Kind(), t.Name())
}
func main() {
checkType(100) // Kind: int, Type: int
checkType("text") // Kind: string, Type: string
checkType([]int{1}) // Kind: slice, Type: slice
}
Note that fmt.Sprintf("%T", ...) returns the type name as a string (e.g., "int", "*MyStruct"), which is sufficient for logging or basic assertions. The reflect approach is necessary when you need to programmatically determine the kind (like reflect.Slice vs reflect.Array) or access type metadata. Avoid using reflection in performance-critical paths, as it is significantly slower than direct type assertions or switch statements on concrete types. If you know the possible types at compile time, prefer a type switch for better performance and readability.