Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Depends. Yes, newtyping is pretty awful in TS due to its structural typing (instead of nominal like Rust for example).

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.



I was recently dealing with some React components at work, where the components would accept as an input the width or the height of the element.

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;


> This naturally raises the question - number of what? Does "50" mean `50px`, `50vw`, `50em`, `50rem`, `50%`?

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


You can do something like this:

    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").


I tried to solve this problem in a way that's reasonably pleasant here https://github.com/spion/branded-types


The pattern you want here is branding.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: