I wanted to check if a supplied type was void
and was surprised to find that when passing in any
a union is returned. Could someone please explain why this is?
type test<T> = T extends void ? void extends T ? 1 : 0 : 0;
type v1 = test<any>; // 1 | 0
type v2 = test<unknown>; // 0
type v3 = test<undefined>; // 0
type v4 = test<null>; // 0
type v5 = test<never>; // never
type v6 = test<void>; // 1
type v7 = test<boolean>; // 0
type v8 = test<string>; // 0
type v9 = test<number>; // 0
type v10 = test<'void'>; // 0
type v11 = test<{}>; // 0
CodePudding user response:
See microsoft/TypeScript#40049 for an authoritative answer. When any
is checked via a conditional type, the result will be the union of both the true and false branches. This may be surprising, but it is working as intended.
Relevant comment:
This isn't especially well-documented outside of the source-code, but in the checker you'll find in the relevant place:
// Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) {
So
any
is treated like a wildcard that matches both branches.
CodePudding user response:
I think I managed to understand this:
void extends any ? 1 : 0
resolves to 1
, meaning void does extend any. I imagine this is a design decision from Typescript authors, I wasn't able to find the reason for this. From my tests - all types extend any
(checked with {}, () => 1, "v", 3, unknown
).
Then, any extends void ? 1 : 0
resolves to 1 | 0
which actually makes sense (it would be one of the two types in the conditional type).
As a result
==> test<any>
=== any extends void ? void extends any ? 1 : 0 : 0
=== any extends void ? 1 : 0
=== 1 | 0