I've seen many different ways to do nominal types in Typescript, but they all seem to fall short in some way. I'd like all these properties to hold:
- Must have clear (not necessarily concise, but bonus points if so) compiler error messages communicating which opaque types, e.g.
Type 'GBP' is not assignable to type 'JPY'
. - Must be truly unique to avoid accidentally matching similar opaque types, i.e. no
__tag__
keys, must useunique symbol
. - Must be able to have safe generic functions taking opaque types sharing the same underlying primitive type, e.g.
<A>(Opaque<number, A>) => Opaque<number, A>
.
More bonus points for a syntactically clean interface, but I understand that's subjective.
This is the best approach I've discovered:
holds, but no bonus points here because you end up with some really nasty error messages containing file paths (live example, see "Errors"):
Newtype<number, typeof import("file:///input").JPY>
. I'm hoping there's a way involving interface extends or similar to make this cleaner.holds because both
Unique.Newtype
andUnique.JPY
areunique symbol
s.holds because we can use the structure of
Newtype
to ensure the types are definitelyNewtype
, due to the fact that it's defined in terms ofUnique.Newtype
which isunique symbol
.