Hacker News new | ask | show | jobs
by spankalee 478 days ago
You can always cast your way around nominal typing, even with enums. So you can do:

    doSomethingBar('No' as Bar);
But you can make my Enum<> utility tighter by including the object type in the brand:

    type Enum<T> = {
      [K in keyof T]: T[K] & {__brand: T};
    }
Then, if you had another const object Baz with the same value as Foo, you would get an error here:

    doSomething(Baz.one);
The only time when you wouldn't get an error there is if the whole Baz enum object was assignable to Foo.
1 comments

> You can always cast your way around nominal typing, even with enums. So you can do: > doSomethingBar('No' as Bar);

I think you can avoid that by not export type `Bar`. I think Bar then acts as an abstract type.

On the other hand with, the branded version, even if you do not avoid exporting the type, even with when branded the object type, you can still get one enum masquerading as another by using the same name. See below where the original Foo is in enums.ts:

    import { Foo as Foo2, doSomething } from './enums'

    // And now, this will work:
    doSomething(Foo2.two);
    // But this will error:
    doSomething('2');
    // this is also an error since the type is not exported
    doSomething('2' as Foo2);


    type Enum<T> = {
      [K in keyof T]: T[K] & {__brand: T};
    }

    const _Foo = {
      one: '1',
      two: '2',
      three: '3'
    } as const;
    export const Foo = _Foo as Enum<typeof _Foo>;
    type ValueOf<X> = X[keyof X];
    type Foo = ValueOf<typeof Foo>;

    // no type error
    doSomething(Foo.two);
I thought enums was the only way to get truly unique types in typescript, but I would be happy to be wrong here.
You can get nominal types with classes by adding a private field, but you can't put private or protected members in a type or interface. `typeof class` might help, but I think they don't want to allow nominal interfaces: https://github.com/microsoft/TypeScript/issues/41824
What if you changed __brand to be a Symbol that was never exported?