Skip to content

Comment on Adding type safety to object IDs in TypeScript

Comments

I’ve done this in multiple languages. I dislike libraries that return string ids.

The proliferation of string identifiers is a pet peeve of mine. It’s what I call “stringly typed” code (not my coinage but I use it all the time).

"Stringly typed", the way I've heard it, is a valid criticism when people replace type safety with magic strings which may or may not be checked at runtime but certainly not at compile time.

However, that's not the case when it comes to Typescript, because literal and union string types are actually checked at compile time. So what is the problem?

If everything is a string, you can accidentally use a UserID as a PostID.

Exactly. Especially easy if the variable name is “id”. For API methods which take multiple ids, the order is easy to mix up - though in TS that is conventionally handled with options objects.

But this is exactly what the article is about? I don't know what you mean by "option object" but it doesn't sound any more conventional than union types to me.

What I meant by option objects is a dictionary object holding all parameters.

doStuff({userId: foo, itemId: bar});

This allows the order of key/val pairs to move around, making it more robust to mistakes than doStuff: (string,string) => void.

What’s your aversion to string ids?

Personally I love them and prefer them in all cases. They aren’t enumerable, never get confused for “is this an array or a map by ID” in PHP, can be used safely as keys without some languages (looking at you PHP) returning an array instead of an object (assoc. array) when converting to JSON, don’t need to be converted back to a number after passing through something like a URL/get param, are less likely to have overlap with keys from other types (even more so if you prefix the key with a type identifier), no need to know that last ID used in the DB so you can build your key in app code instead of the DB, and I’m sure I have more things I like about them.

I understand auto-inc can have some performance gains in the DB but I’ve never needed the gains more than I wanted sane (in my mind) ids.

For the longest time I used UUID (v4) and I still do sometimes but lately I’ve liked KSUID since they are sortable by create date (great for things like DynamoDB IMHO).

Identifiers belong to a domain. A userid belongs to a member of a set of Users, a groupid to Groups, and so on. You are happy to have a User object with a distinct type, and wouldn’t try to have a superset UserOrGroup class.

A string belongs to the domain of “all strings” and so the type system and compiler cannot catch something like “authorizeUser(id,…) where the id is in fact “” or a groupid or “null” or “undefined”.

Lots of code does what you describe. But I prefer to use the type system wherever I can.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.