How to conditionally detect the `any` type in TypeScript?

The expression:

type IsStrictlyAny<T> = (T extends never ? true : false) extends false ? false : true;

meets the criteria:

type IsStrictlyAny<T> = (T extends never ? true : false) extends false ? false : true;

type t1 = IsStrictlyAny<any>;     // true
type t2 = IsStrictlyAny<unknown>; // false
type t3 = IsStrictlyAny<string>;  // false
type t4 = IsStrictlyAny<never>;   // false!

Playground.


This works because T extends never ? true : false when treated as a distributed conditional type resolves to:

  • boolean if T is any.
  • never if T is never.
  • false if T is anything else.

And then since both never and false are assignable to false, the final expression (...) extends false ? false : true is true only when T is any.


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


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:

  1. Check to see if type returns both sides of conditional as a union
  2. Merge the union members into an intersection, and see if the result is 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