Question
How can I find the type of a value in Go?
In Python, I would normally use a built-in operator or function to inspect an object's type. I want to do something similar in Go.
For example, I am iterating through a container like this:
for e := dlist.Front(); e != nil; e = e.Next() {
lines := e.Value
fmt.Printf(reflect.TypeOf(lines))
}
In this loop, I want to determine the type of lines. In my case, the value is expected to be a slice or array of strings, but I am not able to print or inspect its type correctly.
Short Answer
By the end of this page, you will understand how Go represents types at runtime, how to inspect a value's type using reflect.TypeOf, when fmt.Printf("%T", value) is simpler, and how to work with interface values such as e.Value from a container. You will also learn the difference between arrays and slices, plus common mistakes beginners make when checking types in Go.
Concept
In Go, the type of a variable is usually known at compile time. Most of the time, you do not need to ask for a value's type because Go's static type system already enforces it.
However, sometimes you work with values stored in an interface{} value, such as:
- container elements
- decoded JSON data
- generic helper functions
- logging and debugging tools
In those situations, the concrete type is hidden behind the interface, and you may want to inspect it at runtime.
Go provides two common ways to do this:
fmt.Printf("%T", value)to print the typereflect.TypeOf(value)to get areflect.Type
For example:
fmt.Printf("%T\n", value)
or:
t := reflect.TypeOf(value)
fmt.Println(t)
This matters because many Go APIs store values as any or interface{}. When you pull a value back out, you often need to:
- inspect its type
- convert it with a type assertion
- branch with a type switch
A very important detail here is that Go distinguishes between arrays and slices:
Mental Model
Think of an interface{} value in Go like a sealed box with two labels inside:
- the real value
- the real type
When you store something in interface{}, you can no longer see the exact type directly from the variable name alone. You need a tool to peek at the label.
fmt.Printf("%T", value)is like asking, "What type label is on this box?"reflect.TypeOf(value)is like opening a small inspection window to read the type label programmatically- a type assertion like
value.([]string)is like saying, "I believe this box contains a[]string; give it to me as that type"
So when you iterate over e.Value, you are handling boxes. The actual type is still there, but you must inspect or assert it.
Syntax and Examples
Print a value's type
The easiest way to print a type in Go is:
fmt.Printf("%T\n", value)
Example:
package main
import "fmt"
func main() {
x := []string{"a", "b", "c"}
fmt.Printf("%T\n", x)
}
Output:
[]string
Use reflect.TypeOf
If you want the type as a value, use reflect.TypeOf:
package main
import (
"fmt"
"reflect"
)
func main() {
x := []string{"a", "b", "c"}
t := reflect.TypeOf(x)
fmt.Println(t)
}
Step by Step Execution
Consider this example:
package main
import "fmt"
func main() {
var value any = []string{"go", "is", "fun"}
fmt.Printf("%T\n", value)
}
Step by step:
[]string{"go", "is", "fun"}creates a slice of strings.var value any = ...stores that slice inside an interface value.- The interface still remembers the concrete type:
[]string. fmt.Printf("%T\n", value)asks Go to print the concrete type stored in the interface.- The result is:
[]string
Now with reflection:
package main
import (
"fmt"
"reflect"
)
func main() {
var value any = []{, , }
t := reflect.TypeOf(value)
fmt.Println(t)
}
Real World Use Cases
Runtime type inspection is useful in several practical situations:
Working with containers
Packages like container/list store element values as any or interface{}. When reading them back, you may need to inspect or assert the real type.
value := e.Value
fmt.Printf("%T\n", value)
Debugging unknown data
When debugging, printing %T helps you confirm what kind of value you are actually receiving.
fmt.Printf("received type: %T\n", payload)
Handling decoded JSON
When JSON is decoded into map[string]any, fields may contain different runtime types.
var data map[string]any
// after decoding JSON
fmt.Printf("type of age: %T\n", data["age"])
Generic utilities and libraries
Reusable helpers sometimes accept any and branch based on type.
Real Codebase Usage
In real Go codebases, developers usually do more than just print a type.
1. Prefer type assertions when you expect a specific type
If a value should be []string, assert that directly:
lines, ok := e.Value.([]string)
if !ok {
return fmt.Errorf("expected []string, got %T", e.Value)
}
This is common in validation and parsing code.
2. Use %T for logging and debugging
For quick diagnostics, %T is often simpler than reflect.TypeOf:
log.Printf("unexpected type: %T", value)
3. Use type switches for multiple possible types
When several input types are valid, a type switch is clearer:
switch v := value.(type) {
case string:
fmt.Println("single string", v)
case []string:
fmt.Println("many strings", v)
default:
fmt.Printf("unsupported type: %T\n", v)
}
Common Mistakes
Mistake 1: Passing reflect.TypeOf(...) directly to fmt.Printf
Broken code:
fmt.Printf(reflect.TypeOf(lines))
Why it fails:
fmt.Printfexpects the first argument to be a format stringreflect.TypeOf(lines)returns areflect.Type, not a format string
Correct code:
fmt.Println(reflect.TypeOf(lines))
or:
fmt.Printf("%T\n", lines)
Mistake 2: Confusing arrays and slices
Broken assumption:
// expecting array, but actual value is a slice
lines, ok := e.Value.([3]string)
If the stored value is []string, this will fail.
Correct idea:
lines, ok := e.Value.([]string)
Comparisons
| Approach | What it does | Best use case | Example |
|---|---|---|---|
fmt.Printf("%T", v) | Prints the concrete type | Quick debugging and logging | fmt.Printf("%T\n", v) |
reflect.TypeOf(v) | Returns a reflect.Type value | When code needs type metadata | t := reflect.TypeOf(v) |
| Type assertion | Converts from interface to expected type | When you expect one specific type | s, ok := v.([]string) |
| Type switch | Branches across multiple types | When several types are supported | switch v := x.(type) |
Cheat Sheet
// Print the type of a value
fmt.Printf("%T\n", value)
// Get the type as a reflect.Type
import "reflect"
t := reflect.TypeOf(value)
fmt.Println(t)
// Safe type assertion
lines, ok := value.([]string)
if !ok {
fmt.Printf("unexpected type: %T\n", value)
}
// Type switch
switch v := value.(type) {
case string:
fmt.Println("string", v)
case []string:
fmt.Println("slice of strings", v)
default:
fmt.Printf("unknown type: %T\n", v)
}
Key rules:
- Use
%Twhen you just want to print a type. - Use
reflect.TypeOfwhen you need the type programmatically. - Use type assertions when you expect a specific concrete type.
- Use a type switch when multiple types are valid.
fmt.Printfneeds a format string first.- Arrays and slices are different types.
[]stringis a slice,[3]stringis an array.- Values from
container/listare stored asany/ and often need assertion.
FAQ
How do I print the type of a variable in Go?
Use:
fmt.Printf("%T\n", value)
This prints the concrete runtime type.
What is the Go equivalent of Python type inspection?
The closest common options are:
fmt.Printf("%T", value)for printingreflect.TypeOf(value)for getting a type value
Why does fmt.Printf(reflect.TypeOf(x)) not work?
Because fmt.Printf expects a format string as the first argument. Use:
fmt.Println(reflect.TypeOf(x))
or:
fmt.Printf("%T\n", x)
How do I know if a value is a []string in Go?
Use a type assertion:
lines, ok := value.([]string)
If ok is true, the value is a []string.
Mini Project
Description
Build a small Go program that stores different kinds of values in a container/list and then inspects each item's type. This project demonstrates how runtime type inspection works when values are stored as any, and how to safely handle expected types such as []string.
Goal
Create a program that iterates through a list, prints each element's type, and safely processes values that are slices of strings.
Requirements
- Create a
container/listand add at least three different kinds of values. - Iterate through the list and print the type of each value.
- Use a type assertion to detect whether a value is a
[]string. - If the value is a
[]string, print its contents. - If the value is not a
[]string, print a message showing the actual type.
Keep learning
Related questions
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.
Convert String to Integer in Go: Idiomatic Parsing with strconv.Atoi
Learn the idiomatic way to convert a string to an int in Go using strconv.Atoi, with examples, errors, and common mistakes.