Go Type Assertion
Type assertion extracts the concrete value stored inside an interface variable. When a value is stored as an interface, its specific type information is still there but hidden. Type assertion reveals and retrieves it.
Basic Type Assertion
package main
import "fmt"
func main() {
var i interface{} = "Hello, Go!"
s := i.(string) // assert that i holds a string
fmt.Println(s) // Hello, Go!
}
Type Assertion Syntax
value := interfaceVariable.(ConcreteType)
│ │
│ └── the type being asserted
└──────────────────── the interface variable
If the assertion is correct → returns the concrete value
If the assertion is wrong → panics at runtime
Safe Type Assertion (Two-Value Form)
The safe form returns two values: the concrete value and a boolean indicating success. This prevents a panic if the assertion is wrong.
package main
import "fmt"
func main() {
var i interface{} = 42
s, ok := i.(string) // try to assert as string
fmt.Println(s, ok) // "" false — i holds an int, not string
n, ok2 := i.(int) // try to assert as int
fmt.Println(n, ok2) // 42 true
}
Type Assertion vs Type Conversion
| Feature | Type Assertion | Type Conversion |
|---|---|---|
| Purpose | Extract concrete type from interface | Convert one type to another |
| Syntax | x.(T) | T(x) |
| Works on | Interface variables only | Compatible concrete types |
| Panic risk | Yes — use two-value form | No (compile-time checked) |
Type Switch
A type switch checks an interface variable against multiple types in sequence — a clean alternative to chaining type assertions.
package main
import "fmt"
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
case bool:
fmt.Printf("Boolean: %t\n", v)
case []int:
fmt.Printf("Int slice: %v\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
func main() {
describe(42)
describe("Go")
describe(true)
describe([]int{1, 2, 3})
}
Output:
Integer: 42
String: Go
Boolean: true
Int slice: [1 2 3]
Practical Use – Interface Conversion in a Function
package main
import "fmt"
type Animal interface {
Sound() string
}
type Dog struct{ Name string }
type Cat struct{ Name string }
func (d Dog) Sound() string { return "Woof" }
func (c Cat) Sound() string { return "Meow" }
func greet(a Animal) {
if dog, ok := a.(Dog); ok {
fmt.Printf("Good dog, %s! You say: %s\n", dog.Name, dog.Sound())
} else if cat, ok := a.(Cat); ok {
fmt.Printf("Nice cat, %s! You say: %s\n", cat.Name, cat.Sound())
}
}
func main() {
greet(Dog{Name: "Bruno"})
greet(Cat{Name: "Whiskers"})
}
Output:
Good dog, Bruno! You say: Woof
Nice cat, Whiskers! You say: Meow
Key Points
- Type assertion extracts the concrete value stored inside an interface
- The single-value form
x.(T)panics if the assertion fails - The two-value form
v, ok := x.(T)is safe — checkokbefore usingv - A type switch handles multiple possible types cleanly without chained assertions
