DEV Community

Acid Coder
Acid Coder

Posted on • Edited on

Typescript WTF Moment 12: Beware of Object Literal Unions

type ABC = { A: number, B: number } | { C: number } const abc: ABC = { A: 1, B: 2, C: 3 } // no error! 
Enter fullscreen mode Exit fullscreen mode

Image description

playground

Typescript excess property check allow any property from unions of object literal

Solutions:

Assign type never to excess properties and make them optional

type ABC = { A: number, B: number, C?: never } | { A?: never, B?: never, C: number } const abc: ABC = { A: 1, B: 2, C: 3 } // error! const ab: ABC = { A: 1, B: 2 } // ok! const c: ABC = { C: 3 } // ok! const bc: ABC = { B: 2, C: 3 } // error! 
Enter fullscreen mode Exit fullscreen mode

Image description

playground

With common properties that have different types, Typescript is able to discriminate unions

However it is a lot of work if we have a large unions of object literal

We need to create an utility type that automate this process

type GetAllKeys<T> = T extends T ? keyof T : never type CreateOptionalExcessProperty<T, Y> = T extends T ? T & Partial<Record<Exclude<GetAllKeys<Y>, keyof T>, never>> : never type StrictUnion<T> = CreateOptionalExcessProperty<T, T> type ABC = StrictUnion<{ A: number, B: number } | { C: number }>; const abc: ABC = { A: 1, B: 2, C: 3 } // error! const ab: ABC = { A: 1, B: 2 } // ok! const c: ABC = { C: 3 } // ok! const bc: ABC = { B: 2, C: 3 } // error! 
Enter fullscreen mode Exit fullscreen mode

Image description

playground

The purpose of T extends T is to distribute unions

credit

Top comments (0)