Unknown Types in Go are the Worst Chris Coyier

Publish date: 2024-05-27

Say you have some data and it’s either an int or a string (of an int).

thing1 := thing{num: 2} thing2 := thing{num: "31"}Code language: Go (go)

Well, that sucks.

Go is a typed language and this bit of data really should be properly an int. But, ya know, the world is a mess sometimes. Perhaps you have a giant database of JSON data put in there over a decade and this num value is sometimes a proper int and sometimes a string-of-an-int. We might have to json.Unmarshal that data, and the only way we’ll be able to do that safely is a forgiving type. Poop happens.

Assuming we can’t up and fix the data in the database right this second, we have to code around it. So our thing type needs to be forgiving:

type thing struct { num interface{} }Code language: PHP (php)

YEAH FINE.

But this problem is going to keeping spreading its ugly tentacles. There is a good chance that num really wants to be an int and when it interacts with other code, the expectation that it is an int will be enforced.

Maybe we just have the world’s simplest little function that prints the int it accepts:

func printNumber(i int) { fmt.Println(i) }

Can we use it with our data?

printNumber(thing1.num) printNumber(thing2.num)Code language: CSS (css)

Absolutely not. We don’t have an int, we have an interface{}. In JavaScript, we could toss whatever through parseInt() and it will come out as an integer. No such loosey-goosey action in Go.

So, we’ll need to look at the value, check the type, and then really explicitly perform a conversion based on the type we find.

func intFromInterface(v interface{}) (int, error) { switch v := v.(type) { case int: return int(v), nil case string: c, err := strconv.Atoi(v) if err != nil { return 0, err } return c, nil default: return 0, fmt.Errorf("conversion to int from %T not supported", v) } }Code language: PHP (php)

You might want to add more types to that if your data has more potential types. Floats and whatnot.

That’s a big messy function if you ask me, but it’s better to get the data converted into the type it should be in as early as possible, rather than passing it around as an interface{} and passing the buck on converting the type to other places.

Here’s a playground link of all that.

https://twitter.com/Robb0wen/status/1536404941086019586

This is a pain. One thing I’ve found handy for this is using custom JSON unmarshalling functions to map inconsistent datas type in the json, to a proper type in the go struct. Here’s a solid post about the technique https://t.co/pIYNSVscB0

— Shayne O'Sullivan (@ShayneOSullivan) June 13, 2022

ncG1vNJzZmibmKe2tK%2FOsqCeql6jsrV7kWlpa2dga3xyfo6upaSmn6y7bsDYqZysZZmjeqi7jJqpnmWknbJuw86rqq1n