void
and any
are not mutually exclusive: a void
is also considered an any
.
This can be observed by combining them in a union type. If they were truly exclusive, void
would not remain when only any
is present:
type T = void | any // results in any
Due to this, it makes sense that your IsAny
resolves to true
.
On the other hand, for your IsVoid
type, it evaluates to boolean
, which essentially represents true | false
. By substituting true
and false
with something else like 1
and 2
, you will end up with 1 | 2
.
This behavior stems from the fact that the any
type could either be exactly void
or something different than void
. Therefore, we cannot definitively state if it extends void
or not.
If you wish to determine whether a type is specifically any
with no additional characteristics, or precisely void
without any adjustments, you can utilize the following approach (derived from this provided solution):
type IsStrictAny<T> = 0 extends (1 & T) ? T : never;
type A = IsStrictAny<any> // results in any
type B = IsStrictAny<void> // leads to never
type IsVoid<T> = T extends void ? T : never
type IsNotStrictAny<T> = T extends IsStrictAny<T> ? never : T
type IsStrictVoid<T> = IsVoid<T> & IsNotStrictAny<T>
type C = IsStrictVoid<any> // results in never
type D = IsStrictVoid<void> // results in void