type-plus declares typescript as an optional peer dependency and supports TypeScript 5.4
and above. Package managers do not install it for you, so a project that only calls the runtime
functions does not pull in a compiler. See TypeScript version compatibility
for the versions covered by the test matrix.
Note that intersection type checks only works at first level.
It cannot be check recursively,
or else will run into infinite recursion if the type includes recursive types.
@example
type R =IsEqual<1, 1> // true
type R =IsEqual<any, any> // true
type R =IsEqual<boolean, boolean> // true
type R =IsEqual<[1], [1]> // true
type R =IsEqual<boolean, true> // false
type R =IsEqual<any, 1> // false
type R =IsEqual<[any], [1]> // false
type R =IsEqual<{ a:1 }, { a:1; b:2 }> // false
type R =IsEqual<never, undefined> // false
type R =IsEqual<string|undefined, undefined> // false
symbol gets no special treatment: two distinct unique symbols are not equal,
and a unique symbol is not equal to symbol.
Before 8.0.0, IsEqual reported both of those as equal.
@example
declare const s1:uniquesymbol
declare const s2:uniquesymbol
type R =IsEqual<typeof s1, typeof s1> // true
type R =IsEqual<typeof s1, typeof s2> // false
type R =IsEqual<typeof s1, symbol> // false
🔢 customize
Filter to ensure A equals B, otherwise returns never.
@example
type R =IsEqual<1, 1, { selection:'filter' }> // 1
type R =IsEqual<1, number, { selection:'filter' }> // never
🔢 customize
Override the branches directly.
@example
type R =IsEqual<1, 1, { $then:'yes'; $else:'no' }> // 'yes'
type R =IsEqual<1, 2, { $then:'yes'; $else:'no' }> // 'no'
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsEqual<undefined, undefined, IsEqual.$Branch> // $Then
type R =IsEqual<string, undefined, IsEqual.$Branch> // $Else
Note that intersection type checks only works at first level.
It cannot be check recursively,
or else will run into infinite recursion if the type includes recursive types.
@example
type R =IsEqual<1, 1> // true
type R =IsEqual<any, any> // true
type R =IsEqual<boolean, boolean> // true
type R =IsEqual<[1], [1]> // true
type R =IsEqual<boolean, true> // false
type R =IsEqual<any, 1> // false
type R =IsEqual<[any], [1]> // false
type R =IsEqual<{ a:1 }, { a:1; b:2 }> // false
type R =IsEqual<never, undefined> // false
type R =IsEqual<string|undefined, undefined> // false
symbol gets no special treatment: two distinct unique symbols are not equal,
and a unique symbol is not equal to symbol.
Before 8.0.0, IsEqual reported both of those as equal.
@example
declare const s1:uniquesymbol
declare const s2:uniquesymbol
type R =IsEqual<typeof s1, typeof s1> // true
type R =IsEqual<typeof s1, typeof s2> // false
type R =IsEqual<typeof s1, symbol> // false
🔢 customize
Filter to ensure A equals B, otherwise returns never.
@example
type R =IsEqual<1, 1, { selection:'filter' }> // 1
type R =IsEqual<1, number, { selection:'filter' }> // never
🔢 customize
Override the branches directly.
@example
type R =IsEqual<1, 1, { $then:'yes'; $else:'no' }> // 'yes'
type R =IsEqual<1, 2, { $then:'yes'; $else:'no' }> // 'no'
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsEqual<undefined, undefined, IsEqual.$Branch> // $Then
type R =IsEqual<string, undefined, IsEqual.$Branch> // $Else
Note that intersection type checks only works at first level.
It cannot be check recursively,
or else will run into infinite recursion if the type includes recursive types.
@example
type R =IsEqual<1, 1> // true
type R =IsEqual<any, any> // true
type R =IsEqual<boolean, boolean> // true
type R =IsEqual<[1], [1]> // true
type R =IsEqual<boolean, true> // false
type R =IsEqual<any, 1> // false
type R =IsEqual<[any], [1]> // false
type R =IsEqual<{ a:1 }, { a:1; b:2 }> // false
type R =IsEqual<never, undefined> // false
type R =IsEqual<string|undefined, undefined> // false
symbol gets no special treatment: two distinct unique symbols are not equal,
and a unique symbol is not equal to symbol.
Before 8.0.0, IsEqual reported both of those as equal.
@example
declare const s1:uniquesymbol
declare const s2:uniquesymbol
type R =IsEqual<typeof s1, typeof s1> // true
type R =IsEqual<typeof s1, typeof s2> // false
type R =IsEqual<typeof s1, symbol> // false
🔢 customize
Filter to ensure A equals B, otherwise returns never.
@example
type R =IsEqual<1, 1, { selection:'filter' }> // 1
type R =IsEqual<1, number, { selection:'filter' }> // never
🔢 customize
Override the branches directly.
@example
type R =IsEqual<1, 1, { $then:'yes'; $else:'no' }> // 'yes'
type R =IsEqual<1, 2, { $then:'yes'; $else:'no' }> // 'no'
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsEqual<undefined, undefined, IsEqual.$Branch> // $Then
type R =IsEqual<string, undefined, IsEqual.$Branch> // $Else
The predicate types share one convention: instead of collapsing to boolean, they let you
choose what each branch returns, and how the special types any, never and unknown are
treated:
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
IsString<number>
type B =false
// Unions distribute, so a mixed union answers `boolean`
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
IsString<string|1>
type C =boolean
// The special types get their own branches. `any` answers `false` by default,
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
Filter to ensure T is string or string literals, otherwise returns never.
@example
type R =IsString<string, { selection:'filter' }> // string
type R =IsString<'a', { selection:'filter' }> // 'a'
type R =IsString<never, { selection:'filter' }> // never
type R =IsString<unknown, { selection:'filter' }> // never
type R =IsString<string|boolean, { selection:'filter' }> // string
🔢 customize:
Disable distribution of union types.
type R =IsString<string|1> // boolean
type R =IsString<string|1, { distributive:false }> // false
🔢 customize
Use unique branch identifiers to allow precise processing of the result.
@example
type R =IsString<string, IsString.$Branch> // $Then
type R =IsString<bigint, IsString.$Branch> // $Else
Without options, it answers through $Special.Values, skipping the options machinery,
which costs a fraction of the instantiations. The spec pins that shortcut to the full path.
IsString<any, {
$any: 1
$any:1 }>
type E =1
This is the single idea that makes the library composable. Read
Type branching next, then
Options for the full list.