When you create two type aliases for string
for example, type assertions correctly distinguish between the different type aliases.
package main
import (
"fmt"
)
type Type1 string
type Type2 string
func main() {
var v any = Type1("hello")
checkType[Type1]("Type1", v)
checkType[Type2]("Type2", v)
}
func checkType[T any](name string, v any) {
_, ok := v.(T)
fmt.Printf("type is %s: %t\n", name, ok)
}
// Outputs:
// type is Type1: true
// type is Type2: false
But when creating type aliases for interfaces like error
, the type assertions fail.
package main
import (
"errors"
"fmt"
)
type Type1 error
type Type2 error
func main() {
var v any = Type1(errors.New("hello"))
checkType[Type1]("Type1", v)
checkType[Type2]("Type2", v)
}
func checkType[T any](name string, v any) {
_, ok := v.(T)
fmt.Printf("type is %s: %t\n", name, ok)
}
// Outputs:
// type is Type1: true
// type is Type2: true
Why does the type assertion fail when aliasing interface types but with specific types, it does not?
I ran into this issue multiple times when building more complex error handlings based on error types and error type assertions. My solution was mostyl to create a struct which wraps the error and which implements error
and Unwrap()
. How do you distinguish between different error types in your code base?
Would be really interested in an explanation why Go behaves this way and what is an “idiomatic” solution to this.