typescript-book icon indicating copy to clipboard operation
typescript-book copied to clipboard

Should not suggest Enums to implement Brands

Open jack-williams opened this issue 5 years ago • 2 comments

Enums should not be suggested as a branding mechanism because they will be normalised away in union types. The current example:

// FOO
enum FooIdBrand {}
type FooId = FooIdBrand & string;

// BAR
enum BarIdBrand {}
type BarId = BarIdBrand & string;

/**
 * Usage Demo
 */
var fooId: FooId;
var barId: BarId;

// Safety!
fooId = barId; // error
barId = fooId; // error

// Newing up
fooId = 'foo' as FooId;
barId = 'bar' as BarId;

// Both types are compatible with the base
var str: string;
str = fooId;
str = barId;

Consider this usage:

const test: 10 = Math.random() > 0.5 ? 10 : barId;

Or this usage:

const test2: never = Math.random() > 0.5 ? fooId : barId;

These are both unsafe!

The issue is that the types expand as:

/* 
 
 For example `test`

 (10 | BarId) ====> 10 | (BarIdBrand & string)

 Empty literal intersections in unions reduce to never.

 (10 | (BarIdBrand & string)) ====> (10 | never) ====> 10

 */

jack-williams avatar Apr 16 '19 09:04 jack-williams

Nice analysis. Note to self:

/*  
 For example `test2`

 (FooId | BarId) ====> (FooIdBrand & string) | (BarIdBrand & string)

 Empty literal intersections in unions reduce to never.

 ((FooIdBrand & string) | (BarIdBrand & string)) ====> (never | never) ====> never
 */

basarat avatar Apr 17 '19 05:04 basarat

Note to self Review: https://github.com/Microsoft/TypeScript/issues/202

basarat avatar Apr 17 '19 06:04 basarat