TypeScript Type Toolbelt
ts-toolbelt is a comprehensive collection of over 200 advanced type utilities for TypeScript, serving as a 'Lodash for types'. It enables complex type computations, transformations, and creations, abstracting away intricate conditional, mapped, and recursive type definitions. Currently at version 9.6.0, the library maintains an active development pace with releases tied to TypeScript's breaking changes, adhering to semantic versioning. Its key differentiators include an extensive suite of rigorously tested utilities, robust design for manipulating object, union, function, and literal types, and a commitment to providing a standardized API for enhancing type safety in large-scale TypeScript projects. It aims to improve type correctness and introduce new features to the TypeScript type system itself, trading compilation CPU/RAM for higher type safety.
Common errors
-
TS2589: Type instantiation is excessively deep and possibly infinite.
cause Using overly complex or recursive type definitions, often when combining multiple ts-toolbelt utilities, can exceed TypeScript's type instantiation depth limit.fixRefactor your type logic to be less deeply nested or recursive. Break down large type transformations into smaller, intermediate types. Consider if the complexity is truly necessary. -
TS2307: Cannot find module 'ts-toolbelt' or its corresponding type declarations.
cause The ts-toolbelt package is not installed, or your TypeScript configuration cannot find its declaration files (`.d.ts`).fixRun `npm install ts-toolbelt --save-dev` (or `--save`) and ensure your `tsconfig.json` correctly includes `node_modules` (usually default) and has a compatible `target` and `module`. -
TS2339: Property 'Merge' does not exist on type 'typeof import("ts-toolbelt")'. Did you mean 'Object'?cause Attempting to import a specific utility type (like `Merge`) directly from the top-level `ts-toolbelt` package instead of from its designated namespace (e.g., `Object`).fixImport the relevant namespace first, then access the utility. For example, use `import { Object } from 'ts-toolbelt'; type Merged = Object.Merge<...>;` or `import { O } from 'ts-toolbelt'; type Merged = O.Merge<...>;`.
Warnings
- breaking ts-toolbelt's major versions often align with breaking changes in TypeScript. For ts-toolbelt 9.x.x, TypeScript 4.1.0 or higher is required. Using an older TypeScript version can lead to compilation errors or incorrect type inference.
- gotcha Many advanced utilities in ts-toolbelt rely on TypeScript's strict mode, particularly `strictNullChecks: true`. Without it, certain types may behave unexpectedly or lead to less precise results.
- gotcha When working with complex or deeply recursive type manipulations, TypeScript may report `Type instantiation is excessively deep and possibly infinite` errors (TS2589). This indicates that the compiler cannot resolve the type within its internal recursion limits.
- gotcha Do not attempt to `require()` ts-toolbelt or its modules in CommonJS environments. ts-toolbelt consists purely of TypeScript types and has no runtime JavaScript output. Attempting `const { O } = require('ts-toolbelt');` will result in a runtime error or `undefined`.
Install
-
npm install ts-toolbelt -
yarn add ts-toolbelt -
pnpm add ts-toolbelt
Imports
- Object.Merge
import { Merge } from 'ts-toolbelt';import { Object } from 'ts-toolbelt'; type MergedType = Object.Merge<{ a: string }, { b: number }>; - O.Merge
const O = require('ts-toolbelt').O;import { O } from 'ts-toolbelt'; type MergedType = O.Merge<{ a: string }, { b: number }>; - U.Exclude
import type { Exclude } from 'ts-toolbelt';import { U } from 'ts-toolbelt'; type MyUnion = 'a' | 'b' | 'c'; type ExcludedUnion = U.Exclude<MyUnion, 'b'>;
Quickstart
npm install ts-toolbelt --save-dev
# For best results, ensure tsconfig.json includes:
# {
# "compilerOptions": {
# "strictNullChecks": true,
# "strict": true,
# "lib": ["es2015"]
# }
# }
import { O, L, U } from 'ts-toolbelt';
// 1. Merge two object types, handling optional properties gracefully
type User = { id: string; name?: string; };
type Address = { street: string; zip: number; };
type MergedUserAddress = O.Merge<User, Address>;
// Expected: { id: string; name?: string; street: string; zip: number; }
// 2. Append an element to a tuple type
type MyTuple = [1, 2];
type AppendedTuple = L.Append<MyTuple, 3>;
// Expected: [1, 2, 3]
// 3. Exclude types from a union
type EventStatus = 'pending' | 'success' | 'failed' | 'cancelled';
type ActiveStatus = U.Exclude<EventStatus, 'failed' | 'cancelled'>;
// Expected: 'pending' | 'success'
interface Config {
theme: 'dark' | 'light';
version: number;
options?: { debug: boolean; };
}
// 4. Update a nested property (requires Object.Path and Object.Update)
import { Object } from 'ts-toolbelt';
type UpdatedConfig = Object.Update<Config, ['options', 'debug'], true>;
// Expected: { theme: 'dark' | 'light'; version: number; options?: { debug: true; }; }