Issue
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
Solution
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
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
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.