Skip to content

IfNotAnyOrNever: Lazily evaluate different conditional branches#1462

Merged
sindresorhus merged 9 commits into
mainfrom
fix/if-not-any-or-never-lazy-evaluation
Jun 22, 2026
Merged

IfNotAnyOrNever: Lazily evaluate different conditional branches#1462
sindresorhus merged 9 commits into
mainfrom
fix/if-not-any-or-never-lazy-evaluation

Conversation

@som-sm

@som-sm som-sm commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Consider the following example:

type CrashIfAnyWrapper<T> = IfNotAnyOrNever<T, CrashIfAny<T>, 'any handled', 'never handled'>;

type CrashIfAny<T, Acc extends unknown[] = []> = 0 extends 1 & T // Check if `T` is `any`
	? CrashIfAny<T, [...Acc, unknown]>
	: never;

// @ts-expect-error
type T1 = CrashIfAny<any>;

type T2 = CrashIfAnyWrapper<any>; // Should not error, but errors!

CrashIfAny is a type that fails when instantiated with any. To fix this, we try wrapping it in IfNotAnyOrNever to handle the any case explicitly, but this currently doesn't prevent the error. This happens because when we write IfNotAnyOrNever<T, CrashIfAny<T>, 'any handled'>, CrashIfAny is instantiated with T even if its value isn't going to be used.


Couldn't really find a great fix for this, but accepting the values for different cases in an object seems to fix this issue. It seems to make the compiler not do the instantiation until the value is actually accessed.

type CrashIfAnyWrapper<T> = IfNotAnyOrNever<T, {ifNot: CrashIfAny<T>; ifAny: 'any handled'; ifNever: 'never handled'}>;

type CrashIfAny<T, Acc extends unknown[] = []> = 0 extends 1 & T // Check if `T` is `any`
	? CrashIfAny<T, [...Acc, unknown]>
	: never;

// @ts-expect-error
type T1 = CrashIfAny<any>;

type T2 = CrashIfAnyWrapper<any>; // Works!

@som-sm som-sm marked this pull request as draft June 19, 2026 17:27
Repository owner deleted a comment from claude Bot Jun 20, 2026
Comment thread source/internal/type.d.ts
```
// When `T` is a NOT `any` or `never` (like `string`) => Returns `IfNotAnyOrNever` branch
type A = IfNotAnyOrNever<string, 'VALID', 'IS_ANY', 'IS_NEVER'>;
type A = IfNotAnyOrNever<string, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm... would this be better?

Suggested change
type A = IfNotAnyOrNever<string, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;
type A = IfNotAnyOrNever<string, {neither: 'VALID'; any: 'IS_ANY'; never: 'IS_NEVER'}>;

Repository owner deleted a comment from claude Bot Jun 20, 2026
Repository owner deleted a comment from claude Bot Jun 20, 2026
Repository owner deleted a comment from claude Bot Jun 20, 2026
@som-sm som-sm marked this pull request as ready for review June 20, 2026 17:15
@som-sm som-sm requested a review from sindresorhus June 20, 2026 17:15
abo-L

This comment was marked as spam.

Comment thread test-d/internal/if-not-any-or-never.ts
chatman-media added a commit to chatman-media/type-fest that referenced this pull request Jun 22, 2026
Revert the `IfNotAnyOrNever` wrapper back to the inline `IsAny<TArray> extends true ? any : ...` form from the proposed implementation. The wrapper eagerly instantiates the recursive `SplitOnRestElement` body for `any`, producing "Type instantiation is excessively deep" (only resolved once sindresorhus#1462 changes `IfNotAnyOrNever` to lazily evaluate its branches). The inline short-circuit keeps the body in a non-instantiated branch, so `LastArrayElement<any>` resolves to `any` and the whole suite passes on `main` today without depending on sindresorhus#1462.
Repository owner deleted a comment from claude Bot Jun 22, 2026
Repository owner deleted a comment from claude Bot Jun 22, 2026
@som-sm som-sm requested a review from sindresorhus June 22, 2026 07:37
@sindresorhus sindresorhus merged commit ea1446a into main Jun 22, 2026
7 checks passed
@sindresorhus sindresorhus deleted the fix/if-not-any-or-never-lazy-evaluation branch June 22, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants