4 ms·
> 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 expor
by davorak 2y ago
> 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.
- spankalee 2y agoYou 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 https://github.com/microsoft/TypeScript/issues/41824
- hombre_fatal 2y agoWhat if you changed __brand to be a Symbol that was never exported?