You could perhaps newtype using a class (so you can instanceof) or tag via { unit: 'seconds', value: 30 } but that feels awful and seems to be against the ecosystem established practices.
This is indeed one of my gripes with TS typing. I'm spoiled by other languages, but I understand the design choice.
Originally, the type signature was
type Props = {height: number}
This naturally raises the question - number of what? Does "50" mean `50px`, `50vw`, `50em`, `50rem`, `50%`?I've ended up changing the component to only accept pixels and changed the argument to be a string like this:
type Props = {height: `${number}px`}
Of course, if passing a "0" makes sense, you could also allow that. If you want to also accept, say, `50em`, you could use a union-type for that.I think this could actually work for other units as well. Instead of having `delay(200)`, you could instead have `delay("200ms")`, and have the "ms" validated by type system.
Maybe the future will see this getting more popular:
type WeightUnit = 'g' | 'grams' | 'kg' | ...;
type WeightString = `${number}${WeightUnit}`;
function registerPackage(weight: WeightString): void;Because it's React, the expectation is "px", using a string with a suffix to override it: https://reactjs.org/docs/dom-elements.html#style
export interface OpaqueTag<UID> {
readonly __TAG__: UID;
}
export type WeakOpaque<T, UID> = T & OpaqueTag<UID>;
And then use it like this to create a newtype: export type PublicKey = WeakOpaque<Uint8Array, {readonly PublicKey: unique symbol}>;
To create this newtype, you need to use unsafe casting (`as PublicKey`), but you can use a PublicKey directly in APIs where a Uint8Array is needed (thus "weak opaque").