mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
103 lines
2.7 KiB
TypeScript
103 lines
2.7 KiB
TypeScript
import type {IsNever} from './is-never.d.ts';
|
||
|
||
/**
|
||
An if-else-like type that resolves depending on whether the given `boolean` type is `true` or `false`.
|
||
|
||
Use-cases:
|
||
- You can use this in combination with `Is*` types to create an if-else-like experience. For example, `If<IsAny<any>, 'is any', 'not any'>`.
|
||
|
||
Note:
|
||
- Returns a union of if branch and else branch if the given type is `boolean` or `any`. For example, `If<boolean, 'Y', 'N'>` will return `'Y' | 'N'`.
|
||
- Returns the else branch if the given type is `never`. For example, `If<never, 'Y', 'N'>` will return `'N'`.
|
||
|
||
@example
|
||
```
|
||
import type {If} from 'type-fest';
|
||
|
||
type A = If<true, 'yes', 'no'>;
|
||
//=> 'yes'
|
||
|
||
type B = If<false, 'yes', 'no'>;
|
||
//=> 'no'
|
||
|
||
type C = If<boolean, 'yes', 'no'>;
|
||
//=> 'yes' | 'no'
|
||
|
||
type D = If<any, 'yes', 'no'>;
|
||
//=> 'yes' | 'no'
|
||
|
||
type E = If<never, 'yes', 'no'>;
|
||
//=> 'no'
|
||
```
|
||
|
||
@example
|
||
```
|
||
import type {If, IsAny, IsNever} from 'type-fest';
|
||
|
||
type A = If<IsAny<unknown>, 'is any', 'not any'>;
|
||
//=> 'not any'
|
||
|
||
type B = If<IsNever<never>, 'is never', 'not never'>;
|
||
//=> 'is never'
|
||
```
|
||
|
||
@example
|
||
```
|
||
import type {If, IsEqual} from 'type-fest';
|
||
|
||
type IfEqual<T, U, IfBranch, ElseBranch> = If<IsEqual<T, U>, IfBranch, ElseBranch>;
|
||
|
||
type A = IfEqual<string, string, 'equal', 'not equal'>;
|
||
//=> 'equal'
|
||
|
||
type B = IfEqual<string, number, 'equal', 'not equal'>;
|
||
//=> 'not equal'
|
||
```
|
||
|
||
Note: Sometimes using the `If` type can make an implementation non–tail-recursive, which can impact performance. In such cases, it’s better to use a conditional directly. Refer to the following example:
|
||
|
||
@example
|
||
```
|
||
import type {If, IsEqual, StringRepeat} from 'type-fest';
|
||
|
||
type HundredZeroes = StringRepeat<'0', 100>;
|
||
|
||
// The following implementation is not tail recursive
|
||
type Includes<S extends string, Char extends string> =
|
||
S extends `${infer First}${infer Rest}`
|
||
? If<IsEqual<First, Char>,
|
||
'found',
|
||
Includes<Rest, Char>>
|
||
: 'not found';
|
||
|
||
// Hence, instantiations with long strings will fail
|
||
// @ts-expect-error
|
||
type Fails = Includes<HundredZeroes, '1'>;
|
||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
// Error: Type instantiation is excessively deep and possibly infinite.
|
||
|
||
// However, if we use a simple conditional instead of `If`, the implementation becomes tail-recursive
|
||
type IncludesWithoutIf<S extends string, Char extends string> =
|
||
S extends `${infer First}${infer Rest}`
|
||
? IsEqual<First, Char> extends true
|
||
? 'found'
|
||
: IncludesWithoutIf<Rest, Char>
|
||
: 'not found';
|
||
|
||
// Now, instantiations with long strings will work
|
||
type Works = IncludesWithoutIf<HundredZeroes, '1'>;
|
||
//=> 'not found'
|
||
```
|
||
|
||
@category Type Guard
|
||
@category Utilities
|
||
*/
|
||
export type If<Type extends boolean, IfBranch, ElseBranch> =
|
||
IsNever<Type> extends true
|
||
? ElseBranch
|
||
: Type extends true
|
||
? IfBranch
|
||
: ElseBranch;
|
||
|
||
export {};
|