Getting Started
type-plus provides over 200 utility types and functions for applications, libraries,
and type-level programming.
Installation
Section titled “Installation”npm install type-pluspnpm add type-plusyarn add type-plustype-plus declares typescript as a peer dependency and supports TypeScript 5.6 and
above. See TypeScript version compatibility
for the versions covered by the test matrix.
Your first utility type
Section titled “Your first utility type”Most of type-plus is types, so nothing needs to be imported at runtime:
import type { type IsEqual<A, B, Then = true, Else = false> = [A, B] extends [B, A] ? And<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, { $then: Then; $else: Or<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, { $then: Else; $else: And<$Special<A, { $any: true; $unknown: false; $never: false; $void: false; $else: false; }>, $Special<...>, { ...; }>; }>;}> : And<...>
Checks A and B are equal.
type R = IsEqual<1, 1> // truetype R = IsEqual<any, any> // truetype R = IsEqual<boolean, boolean> // truetype R = IsEqual<true, true> // truetype R = IsEqual<[1], [1]> // true
type R = IsEqual<boolean, true> // falsetype R = IsEqual<any, 1> // falsetype R = IsEqual<[any], [1]> // falsetype R = IsEqual<{ a: 1 }, { a: 1; b: 2 }> // false
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.
IsEqual } from 'type-plus'
type A = type IsEqual<A, B, Then = true, Else = false> = [A, B] extends [B, A] ? And<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, { $then: Then; $else: Or<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, { $then: Else; $else: And<$Special<A, { $any: true; $unknown: false; $never: false; $void: false; $else: false; }>, $Special<...>, { ...; }>; }>;}> : And<...>
Checks A and B are equal.
type R = IsEqual<1, 1> // truetype R = IsEqual<any, any> // truetype R = IsEqual<boolean, boolean> // truetype R = IsEqual<true, true> // truetype R = IsEqual<[1], [1]> // true
type R = IsEqual<boolean, true> // falsetype R = IsEqual<any, 1> // falsetype R = IsEqual<[any], [1]> // falsetype R = IsEqual<{ a: 1 }, { a: 1; b: 2 }> // false
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.
IsEqual<'a', 'a'>type A = true
type B = type IsEqual<A, B, Then = true, Else = false> = [A, B] extends [B, A] ? And<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false;}>, { $then: Then; $else: Or<$Special<A, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, $Special<B, { $any: false; $unknown: false; $never: true; $void: false; $else: false; }>, { $then: Else; $else: And<$Special<A, { $any: true; $unknown: false; $never: false; $void: false; $else: false; }>, $Special<...>, { ...; }>; }>;}> : And<...>
Checks A and B are equal.
type R = IsEqual<1, 1> // truetype R = IsEqual<any, any> // truetype R = IsEqual<boolean, boolean> // truetype R = IsEqual<true, true> // truetype R = IsEqual<[1], [1]> // true
type R = IsEqual<boolean, true> // falsetype R = IsEqual<any, 1> // falsetype R = IsEqual<[any], [1]> // falsetype R = IsEqual<{ a: 1 }, { a: 1; b: 2 }> // false
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.
IsEqual<'a', 'b'>type B = false
Branching options
Section titled “Branching options”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:
import type { type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
IsString } from 'type-plus'
type A = type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
IsString<string>type A = true
type B = type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
IsString<number>type B = false
// Unions distribute, so a mixed union answers `boolean`type C = type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
IsString<string | 1>type C = boolean
// The special types get their own branches. `any` answers `false` by default,type D = type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
IsString<any>type D = false
// unless you say otherwisetype E = type IsString<T, $O extends IsString.$Options = {}> = 0 extends 1 & T ? "$any" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>[Exclude<keyof $O, "$then" | "$else"> & "$any"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$unknown" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$unknown"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$never" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$never"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : [...] extends [...] ? "$void" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>[Exclude<...> & "$void"] : "$then" extends "$then" | "$else" | Exclude<keyof $O, "$then" | "$else"> ? $MergeOptions<$O, { $then: $ResolveBranch<$O, [$Else]>; $else: IsString.$<T, $O>;}>["$then"] : $MergeOptions<...>["selection"] extends "filter" ? T : true : "$else" extends "$then" | ... 1 more ... | Exclude<...> ? $MergeOptions<...>["$else"] : $MergeOptions<...>["selection"] extends "filter" ? never : false
🎭 predicate
Validate if T is string or string literals.
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.
Where to go from here
Section titled “Where to go from here”- API — the utilities, grouped by the type they operate on.
- Categories — what the icons in the docs mean.
- Status — how stability and
sincemarkers are recorded.