Why is `NoInfer` inconsistently required at certain places in type-level TS?

1 day ago 1
ARTICLE AD BOX

Consider the following pretty standard definition for the IsAny type utility:

type IsAny<T> = 0 extends 1 & T ? true : false;

If you now try to use this utility as part of a constraint to a type argument, you would get a circular reference error:

type TShouldNotBeAny<T extends IsAny<T> extends true ? never : unknown> = T; // Errors: Type parameter 'T' has a circular constraint.

To fix this, we need to add a NoInfer in IsAny's definition:

type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false; type TShouldNotBeAny<T extends IsAny<T> extends true ? never : unknown> = T; // No error type T1 = TShouldNotBeAny<string>; // Allowed type T2 = TShouldNotBeAny<number>; // Allowed type T3 = TShouldNotBeAny<any>; // Not allowed as expected. Type 'any' does not satisfy the constraint 'never'.

But surprisingly, a NoInfer is not required in the following IsString case, why?

type IsString<T> = T extends string ? true : false; type TShouldNotBeString<T extends IsString<T> extends true ? never : unknown> = T; // No error type T1 = TShouldNotBeString<number>; // Allowed type T2 = TShouldNotBeString<bigint>; // Allowed type T3 = TShouldNotBeString<string>; // Not allowed as expected. Type 'string' does not satisfy the constraint 'never'.

As far as I understand, the NoInfer shouldn't have been required in the IsAny case as well, because that T isn't an inference point, right? So, NoInfer should mean nothing there.

And, IsAny<T> extends true shouldn't be circular, IsAny should be able to return true or false depending on whatever TShouldNotBeAny is instantiated with.

As far as I understand, circular references should only be when something like T extends T happens, like:

type TShouldNotBeAny<T extends IsAny<T> extends true ? never : T> = T; // Errors // Notice `T` in place of `unknown`

And, the above fails even with NoInfer, and it makes sense, because that NoInfer should not be doing anything meaningful in my understanding. But somehow it does something in the IsAny case.

Read Entire Article