I would like an utility that I can use as IsStrictlyAny<T>
and it will resolve to the type true
if T
is exactly any
and to the false
type otherwise. How can I do this?
My first idea:
type IsStrictlyAny<T> = any extends T ? true : false;
Results:
IsStrictlyAny<any>
: true
(good!)IsStrictlyAny<unknown>
: true
(bad! - I want false
)IsStrictlyAny<string>
: boolean
(bad! - I want false
)Conditional Types in TypeScript. TypeScript 2.8 introduced conditional types, a powerful and exciting addition to the type system. Conditional types let us express non-uniform type mappings, that is, type transformations that differ depending on a condition.
The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type checking during compilation. Let that sink in. The TypeScript documentation express clearly that when we have the any type, we are telling the compiler:
Most of the uses we have seen indicate that we are dealing with the base type in TypeScript land. Ruby’s is Object same for C#, inside the documentation we find: (…) values from code that has been written without TypeScript or a 3rd party library. In these cases, we might want to opt-out of type checking.
When conditional types act on a generic type, they become distributive when given a union type. For example, take the following: type ToArray < Type > = Type extends any ? Type [] : never; If we plug a union type into ToArray, then the conditional type will be applied to each member of that union. type ToArray < Type > = Type extends any ?
This is a good question, and at first I thought it was impossible, but after some investigating, I think there's a way.
First of all, check this out:
type Test = any extends never ? 'A' : 'B' // "A" | "B"
What that means is that typescript knows that any
could be anything, and it therefore cannot decide which side of the conditional to return, so it returns both sides as a union. I'm reasonably certain that any
is the only case that would behave this way.
So then you just need to try to detect if a union was returned or a single value. To do that, we use two tools.
First, note that the intersection of two incompatible types is never
.
type Test = 'A' & 'B' // never
Which makes sense, since a value cannot be two different strings at the same time.
Second, if we can get an intersection of all the members of the type union, we can then test see if it's never
, or it's any other valid type. This answer has a helper to convert a union to an intersection, so I wont bother explaining it.
So to some up:
never
.// From: https://stackoverflow.com/a/50375286/62076
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
// If T is `any` a union of both side of the condition is returned.
type UnionForAny<T> = T extends never ? 'A' : 'B'
// Returns true if type is any, or false for any other type.
type IsStrictlyAny<T> =
UnionToIntersection<UnionForAny<T>> extends never ? true : false
type A = IsStrictlyAny<any> // true
type B = IsStrictlyAny<string> // false
type C = IsStrictlyAny<unknown> // false
Playground
The simplest answer I've found is in the answer to the question this duplicates, and the explanation is in a related answer:
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IsAny<T> = IfAny<T, true, false>;
type A = IsAny<any>; // true
type B = IsAny<unknown>; // false
type C = IsAny<string>; // false
type D = IsAny<never>; // false
The short reason why this works is that 0 extends (1 & T)
should be false for any type T
that "plays by the rules". 0
isn't assignable to 1
so it really shouldn't be assignable to 1 & T
no matter what T
is. But any
doesn't play by the rules: 1 & any
evaluates to any
, and 0 extends any
is true.
Hope that helps; good luck!
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With