Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this Conditional Typing Needed? [duplicate]

Tags:

typescript

I am studying the source code of the ts-toolbelt library. And I often meet this expressionO extends unknown. In my opinion, it does not add any functionality.

And so I wondered, what is it for?

/**
 * @hidden
 */
export type _UnionOf<O extends object> =
    O[keyof O]

/**
 * Transform an [[Object]] into an [[Union]]
 * @param O to transform
 * @returns [[Any]]
 * @example
 * ```ts
 * ```
 */
export type UnionOf<O extends object> =
    O extends unknown
    ? _UnionOf<O>
    : never

For some reason, instead of exporting the _UnionOf type, it is preceded by the expression O extends unknown

like image 736
Alexander Pankin Avatar asked Nov 21 '25 02:11

Alexander Pankin


2 Answers

Presumably the library authors decided that UnionOf<A | B | C> should produce the same result as UnionOf<A> | UnionOf<B> | UnionOf<C>, and the definition of _UnionOf did not do that. The O extends unknown ? ... : never check, which deceptively looks like it does nothing, causes that to happen.

Expressions that look like they do nothing but actually distribute across unions, when T is a generic type parameter:

  • T extends unknown ? ... : never
  • T extends any ? ... : never
  • T extends T ? ... : never

If T is a type parameter, as in the generic function function foo<T>(/*...*/): void, the generic type alias type Foo<T> = /*...*/, or the generic interface interface Foo<T> {/*...*/}, then a type of the form T extends XXX ? YYY : ZZZ is a distributive conditional type.

It distributes the conditional check across unions in T. When T is specified with some specific type, the compiler splits that type up into its union members, evaluates the check for each such member, and then joins the results back into a new union. So if F<T> distributes across unions, then F<A | B | C> will be the same as F<A> | F<B> | F<C>.


Not all type functions are distributive across unions. For example, the keyof operator does not turn unions of inputs into unions of outputs:

type KeyofA = keyof {a: string}; // "a"
type KeyofB = keyof {b: number}; // "b"
type KeyofAorB = keyof ({ a: string } | { b: number }); // never

Similarly, _UnionOf is not distributive across unions (related to the fact that it uses keyof in its definition):

type Oops = _UnionOf<{ a: string } | { b: number }>
// never

If you have a type function which is not distributive in unions and you want it to be, you can wrap it in a distributive conditional type:

type DistribKeyof<T> = T extends unknown ? keyof T : never;

type UnionKeyofAorB = DistribKeyof<{ a: string } | { b: number }>; // "a" | "b"

And therefore UnionOf is distributive across unions:

type Correct = UnionOf<{ a: string } | { b: number }>
// string | number

Playground link to code

like image 90
jcalz Avatar answered Nov 23 '25 16:11

jcalz


O extends unknown ? _UnionOf<O> : never is a conditional type; here it is used as a distributive conditional type. Without using a conditional type, TypeScript will narrow the type to one that applies to all possible union values (often never). With distributive conditional types, it will apply to each potential union value. Though the conditional aspect of distributive conditional types could be used to filter the potential union values, here that is unnecessary, so you'll frequently see values like T extends any (as in the handbook) or T extends unknown here.

As for the difference between those, dragomirtitian describes it in the Typescript gitter.im:

any sometimes deletes information about the type parameter. There was a bug around this I filed myself microsoft/TypeScript#30569. It should work ok now, but I would still recommend one of the other two ways [ed: T extends unknown or T extends T]. Any extends clause in a conditional type introduces information about T, extends any can confuse the compiler about T

See also:

  • What does it mean for a type to distribute over unions?
  • Typescript Distributive Conditional Types
like image 25
Jeff Bowman Avatar answered Nov 23 '25 16:11

Jeff Bowman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!