Home > Mobile >  Typescript literal unions
Typescript literal unions

Time:09-17

I have two literal types, and I want to exclude B from union A.

type A = 'a' | 'b'
type B = 'a'

When I do it like this:

type MyExclude<A2, B2> = A2 extends B2 ? never : A2
type C2 = MyExclude<A, B>
// C2 = 'b'

It works correctly, i.e. i get 'b'.

But when I do it directly like this:

type C = A extends B ? never : A
// C = 'a' | 'b'

It doesn't. Why should it matter whether I do it via a intermediary generic type or directly?

Thanks.

CodePudding user response:

When you do it as a generic, Typescript is going to apply the check distributively to each member of the union. So type C2 = MyExclude<A, B> is going to be evaluated not as 'a | b' extends 'a' ? never : 'a | 'b', but rather ('a' extends 'a' ? never : 'a') | ('b' extends 'a' ? never : 'b'). 'a' does extend B, so that results in never. 'b' does not extend B, so that results in 'b'. And the final result is never | 'b', which is just 'b'.

The distributive part does not happen if it's not a generic. type C = A extends B ? never : A is just going to be evaluated once, as 'a | b' extends 'a' ? never : 'a | 'b'. 'a' | 'b' does not extend 'a', so the result is the whole type, 'a' | 'b'

See distributive conditional types

Distributive conditional types

Conditional types in which the checked type is a naked type parameter are called distributive conditional types. Distributive conditional types are automatically distributed over union types during instantiation. For example, an instantiation of T extends U ? X : Y with the type argument A | B | C for T is resolved as (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y).

  • Related