1. What Is a Type Assertion?
A type assertion is used when you have a value stored inside an interface and you want to check or retrieve its real type.
The syntax is:
value.(Type)
Easy meaning:
"Is the value inside this interface really this Type?"
2. Simplest Example
package main
import "fmt"
func main() {
// 'value' has type 'any'.
//
// any can hold values of different types.
var value any
// Store a string inside 'value'.
value = "Hello Go"
// Type assertion:
//
// value.(string)
//
// means:
//
// "I expect the value stored inside
// this interface to be a string."
text := value.(string)
// 'text' is now a normal string variable.
fmt.Println(text)
// Output:
//
// Hello Go
}
The important line is:
text := value.(string)
Think of it as:
value contains something
↓
value.(string)
↓
"Is that something a string?"
↓
Yes → give me the string
3. Why Do We Need Type Assertions?
package main
import "fmt"
func main() {
// Normal string variable.
name := "Alice"
// Go already knows 'name' is a string.
// Therefore no type assertion is needed.
fmt.Println(len(name))
// --------------------------------
// Here the variable type is 'any'.
var value any = "Alice"
// Go knows that 'value' is an interface,
// but we want to work with the string
// stored inside it.
// Extract the string.
text := value.(string)
// Now text is a normal string.
fmt.Println(len(text))
// Output:
//
// 5
// 5
}
4. Think of an Interface as a Box
package main
import "fmt"
func main() {
// Imagine 'value' as a box.
//
// +----------------------+
// | Type: string |
// | Value: "Go" |
// +----------------------+
var value any = "Go"
// Type assertion asks:
//
// "Does this box contain a string?"
text := value.(string)
// Yes.
// Therefore Go gives us the string.
fmt.Println(text)
// Output:
//
// Go
}
This is the easiest mental model:
Interface = Box
Type assertion = Check what is inside the box
5. What Happens If the Type Is Wrong?
package main
func main() {
// The real value stored here is an int.
var value any = 100
// This would cause a runtime PANIC:
//
// text := value.(string)
//
// Why?
//
// Actual stored type:
//
// int
//
// Requested type:
//
// string
//
// We told Go:
//
// "I am sure this is a string."
//
// But it is not.
_ = value
}
So this form:
text := value.(string)
means:
"I am certain this is a string."
If you are wrong, the program can panic.
6. Safe Type Assertion
This is the form you should learn first.
package main
import "fmt"
func main() {
var value any = "Hello"
// Safe type assertion returns TWO values.
//
// text = extracted string
// ok = whether the assertion succeeded
text, ok := value.(string)
// Check if the assertion failed.
if !ok {
fmt.Println("Value is not a string")
return
}
// If we reach this line,
// 'text' is definitely a string.
fmt.Println(text)
// Output:
//
// Hello
}
Remember this pattern:
value, ok := interfaceValue.(Type)
if !ok {
// wrong type
return
}
// safely use value
7. What Does ok Mean?
package main
import "fmt"
func main() {
var value any = "Go"
text, ok := value.(string)
// Because value contains a string:
//
// text = "Go"
// ok = true
fmt.Println(text)
fmt.Println(ok)
// Output:
//
// Go
// true
}
If it fails:
package main
import "fmt"
func main() {
var value any = 100
text, ok := value.(string)
// value contains int,
// not string.
//
// Therefore:
//
// text = ""
//
// "" is the zero value of string.
//
// ok = false
fmt.Printf("text = %q\n", text)
fmt.Println("ok =", ok)
// Output:
//
// text = ""
// ok = false
}
8. Type Assertion with int
package main
import "fmt"
func main() {
// Store an integer inside an interface.
var value any = 25
// Try to extract an int.
number, ok := value.(int)
if !ok {
fmt.Println("Not an integer")
return
}
// number is now a normal int.
result := number * 2
fmt.Println(result)
// Output:
//
// 50
}
9. Type Assertion with a Struct
package main
import "fmt"
// Normal struct.
type User struct {
Name string
Age int
}
func main() {
// Store a User inside 'any'.
var value any = User{
Name: "Alice",
Age: 30,
}
// Extract User from the interface.
user, ok := value.(User)
if !ok {
fmt.Println("Value is not a User")
return
}
// 'user' is now type User,
// so its fields are available.
fmt.Println(user.Name)
fmt.Println(user.Age)
// Output:
//
// Alice
// 30
}
10. Important: User and *User Are Different
package main
import "fmt"
type User struct {
Name string
}
func main() {
// Store a POINTER to User.
var value any = &User{
Name: "Alice",
}
// This checks for User.
//
// But the stored type is *User.
user1, ok1 := value.(User)
fmt.Println(ok1)
// Output:
//
// false
_ = user1
// --------------------------------
// This checks for *User.
//
// That matches the actual stored type.
user2, ok2 := value.(*User)
fmt.Println(ok2)
fmt.Println(user2.Name)
// Output:
//
// true
// Alice
}
Remember:
User
and:
*User
are different types.
11. Type Assertion Does NOT Convert Types
This is extremely important.
package main
import "fmt"
func main() {
// The actual stored type is float64.
var value any = 10.5
// We ask:
//
// "Is the stored value an int?"
number, ok := value.(int)
// No.
fmt.Println(number)
fmt.Println(ok)
// Output:
//
// 0
// false
}
Type assertion does not mean:
Convert float64 to int
It means:
Check whether the value is already an int
To convert:
package main
import "fmt"
func main() {
var value any = 10.5
// Step 1:
// Assert the REAL stored type.
decimal, ok := value.(float64)
if !ok {
return
}
// Step 2:
// Perform normal type conversion.
number := int(decimal)
fmt.Println(number)
// Output:
//
// 10
}
So:
value.(float64)
is a type assertion.
But:
int(decimal)
is a type conversion.
12. Type Assertion vs Type Conversion
package main
func main() {
// --------------------------
// TYPE CONVERSION
// --------------------------
number := 10
// Convert int into float64.
decimal := float64(number)
_ = decimal
// --------------------------
// TYPE ASSERTION
// --------------------------
var value any = "Go"
// Extract/check the string already
// stored inside the interface.
text := value.(string)
_ = text
}
Remember:
Type Conversion
---------------
int → float64
float64(number)
Type Assertion
--------------
interface → contained type
value.(string)
13. Real Use Case: map[string]any
You may see dynamic data written like this:
package main
import "fmt"
func main() {
// Values can contain different types.
data := map[string]any{
"name": "Alice",
"age": 30,
}
// data["name"] returns an 'any' value.
nameValue := data["name"]
// Extract string.
name, ok := nameValue.(string)
if !ok {
fmt.Println("name is not a string")
return
}
// Extract age.
ageValue := data["age"]
age, ok := ageValue.(int)
if !ok {
fmt.Println("age is not an integer")
return
}
fmt.Println("Name:", name)
fmt.Println("Age:", age)
// Output:
//
// Name: Alice
// Age: 30
}
This is one common use case for type assertions.
14. Checking Multiple Possible Types
You could do this:
package main
import "fmt"
func printValue(value any) {
// Check string.
if text, ok := value.(string); ok {
fmt.Println("String:", text)
return
}
// Check int.
if number, ok := value.(int); ok {
fmt.Println("Integer:", number)
return
}
// Check bool.
if flag, ok := value.(bool); ok {
fmt.Println("Boolean:", flag)
return
}
fmt.Println("Unknown type")
}
func main() {
printValue("Go")
printValue(100)
printValue(true)
// Output:
//
// String: Go
// Integer: 100
// Boolean: true
}
This works.
But when many types are possible, use a type switch.
15. Type Switch
package main
import "fmt"
func printValue(value any) {
// value.(type) checks the concrete type
// stored inside the interface.
switch v := value.(type) {
case string:
// Here v is a string.
fmt.Println("String:", v)
case int:
// Here v is an int.
fmt.Println("Integer:", v)
case bool:
// Here v is a bool.
fmt.Println("Boolean:", v)
case float64:
// Here v is a float64.
fmt.Println("Float:", v)
default:
fmt.Println("Unknown type")
}
}
func main() {
printValue("Go")
printValue(100)
printValue(true)
printValue(3.14)
// Output:
//
// String: Go
// Integer: 100
// Boolean: true
// Float: 3.14
}
Simple rule:
One possible type
-----------------
value, ok := data.(string)
Many possible types
-------------------
switch v := data.(type)
16. Type Assertion with Interfaces
Type assertions can also check whether a value supports another interface.
package main
import "fmt"
// Every Speaker can Speak().
type Speaker interface {
Speak()
}
// Every Runner can Run().
type Runner interface {
Run()
}
type Dog struct{}
func (Dog) Speak() {
fmt.Println("Woof!")
}
func (Dog) Run() {
fmt.Println("Dog is running")
}
func main() {
// Variable only promises Speaker behavior.
var speaker Speaker = Dog{}
speaker.Speak()
// Now ask:
//
// "Does the actual value inside speaker
// also satisfy Runner?"
runner, ok := speaker.(Runner)
if !ok {
fmt.Println("Cannot run")
return
}
// Yes, Dog also has Run().
runner.Run()
// Output:
//
// Woof!
// Dog is running
}
This is a very useful advanced use case.
17. Practical Use Case: Optional Capability
package main
import "fmt"
// Required behavior.
type Saver interface {
Save()
}
// Optional additional behavior.
type Closer interface {
Close()
}
type File struct{}
func (File) Save() {
fmt.Println("Saving file")
}
func (File) Close() {
fmt.Println("Closing file")
}
func process(s Saver) {
// Every Saver can Save().
s.Save()
// But maybe not every Saver can Close().
//
// Check whether this particular value
// also satisfies Closer.
closer, ok := s.(Closer)
if ok {
closer.Close()
}
}
func main() {
file := File{}
process(file)
// Output:
//
// Saving file
// Closing file
}
The assertion:
closer, ok := s.(Closer)
means:
"Does the concrete value inside s
also support Closer?"
18. Type Assertions Only Work on Interfaces
package main
func main() {
// Normal string variable.
name := "Alice"
// This is INVALID:
//
// name.(string)
//
// Why?
//
// name is already a string.
// It is not an interface value.
_ = name
// --------------------------
// This IS an interface.
var value any = "Alice"
// Therefore a type assertion is allowed.
text := value.(string)
_ = text
}
19. Unsafe vs Safe Type Assertion
package main
func main() {
var value any = "Go"
// ------------------------------
// UNSAFE / ONE-VALUE FORM
// ------------------------------
// Use when you are certain.
text := value.(string)
_ = text
// If wrong:
//
// runtime panic
// ------------------------------
// SAFE / TWO-VALUE FORM
// ------------------------------
text2, ok := value.(string)
if !ok {
// Wrong type.
return
}
_ = text2
// If wrong:
//
// no panic
// ok becomes false
}
For learning and most uncertain data, prefer:
value, ok := data.(Type)
20. Final Cheat Sheet
package main
func main() {
// --------------------------------
// Interface containing a string
// --------------------------------
var value any = "Go"
// --------------------------------
// Basic type assertion
// --------------------------------
text := value.(string)
_ = text
// --------------------------------
// Safe type assertion
// --------------------------------
text2, ok := value.(string)
if !ok {
return
}
_ = text2
// --------------------------------
// Type assertion is NOT conversion
// --------------------------------
// Assertion:
//
// value.(string)
//
// means:
//
// "Is a string already inside value?"
// --------------------------------
// Type conversion
// --------------------------------
number := 10
decimal := float64(number)
_ = decimal
// --------------------------------
// Many possible types
// --------------------------------
switch v := value.(type) {
case string:
_ = v
case int:
_ = v
case bool:
_ = v
}
}
The One Definition to Remember
// TYPE ASSERTION:
//
// actualValue, ok := interfaceValue.(ExpectedType)
//
// means:
//
// "Check whether the value stored inside this
// interface has the type I expect.
//
// If yes:
// give me the value
// ok = true
//
// If no:
// give me the zero value
// ok = false"
The Easiest Mental Model
// Imagine:
//
// var value any = "Alice"
//
//
// value is a BOX:
//
// +----------------------+
// | Type: string |
// | Value: "Alice" |
// +----------------------+
//
//
// name, ok := value.(string)
//
// means:
//
// "Open the box.
//
// Is the value inside really a string?"
//
//
// YES:
//
// name = "Alice"
// ok = true
//
//
// NO:
//
// name = ""
// ok = false
In one sentence: A type assertion checks or extracts the actual value/type stored inside a Go interface.