ts-toolkit для TypeScript: полное руководство
1. Что это и зачем нужна
ts-toolkit — это мощная библиотека утилит для TypeScript, предназначенная для упрощения повседневных задач разработчика. Она предоставляет набор готовых функций, типов и классов, которые помогают работать с объектами, массивами, строками, функциями и типами данных более эффективно и безопасно. В отличие от стандартной библиотеки JavaScript, ts-toolkit предлагает строго типизированные решения, полностью совместимые с системой типов TypeScript.
Зачем нужна эта библиотека? В современной TypeScript-разработке часто возникает необходимость в выполнении рутинных операций: глубокое копирование объектов, безопасная навигация по вложенным структурам, проверка типов во время выполнения, работа с Option/Result (монады для обработки ошибок) и многое другое. ts-toolkit берёт на себя реализацию этих паттернов, позволяя разработчику сосредоточиться на бизнес-логике. Библиотека особенно полезна в проектах, где требуется высокая надёжность и строгая типизация.
Ключевое преимущество ts-toolkit — это безопасность типов (type safety). Каждая функция спроектирована так, чтобы максимально использовать возможности TypeScript: условные типы, mapped types, infer и другие продвинутые конструкции. Это означает, что многие ошибки, которые могли бы проявиться только в рантайме, будут обнаружены на этапе компиляции.
2. Установка
Установка библиотеки производится через менеджер пакетов npm:
npm install @tool-belt/ts-toolkit
Или через yarn:
yarn add @tool-belt/ts-toolkit
После установки импортируйте необходимые функции в ваш проект:
import { deepClone, safeGet, pipe } from '@tool-belt/ts-toolkit';
3. Быстрый старт
Минимальный рабочий пример, демонстрирующий основные возможности библиотеки:
import { deepClone, isObject, pipe } from '@tool-belt/ts-toolkit';
// Пример 1: Глубокое копирование
const original = { a: 1, b: { c: 2 } };
const cloned = deepClone(original);
cloned.b.c = 42;
console.log(original.b.c); // 2 (оригинал не изменился)
// Пример 2: Проверка типа
console.log(isObject({})); // true
console.log(isObject(null)); // false
// Пример 3: Композиция функций
const addOne = (x: number) => x + 1;
const double = (x: number) => x * 2;
const addOneThenDouble = pipe(addOne, double);
console.log(addOneThenDouble(5)); // 12
4. Основные методы и функции
4.1. deepClone
Сигнатура: function deepClone<T>(value: T): T
Описание: Выполняет глубокое копирование любого значения, включая вложенные объекты, массивы, даты, Map, Set и другие структуры. Поддерживает циклические ссылки.
import { deepClone } from '@tool-belt/ts-toolkit';
const user = {
name: 'Alice',
address: { city: 'NYC', zip: 10001 },
hobbies: ['reading', 'coding']
};
const clonedUser = deepClone(user);
clonedUser.address.city = 'LA';
console.log(user.address.city); // 'NYC'
4.2. safeGet
Сигнатура: function safeGet<T, K extends keyof T>(obj: T, key: K): T[K] | undefined
Описание: Безопасно получает значение свойства объекта. Возвращает undefined, если свойство отсутствует или объект равен null/undefined, вместо выбрасывания ошибки.
import { safeGet } from '@tool-belt/ts-toolkit';
const data = { user: { name: 'Bob' } };
const name = safeGet(data, 'user')?.name; // 'Bob'
const missing = safeGet(null, 'anything'); // undefined
4.3. pipe
Сигнатура: function pipe<A, B, C>(f1: (a: A) => B, f2: (b: B) => C): (a: A) => C (и перегрузки для большего числа функций)
Описание: Создаёт композицию функций, передавая результат каждой функции как аргумент следующей. Выполняется слева направо.
import { pipe } from '@tool-belt/ts-toolkit';
const trim = (s: string) => s.trim();
const toUpper = (s: string) => s.toUpperCase();
const exclaim = (s: string) => s + '!';
const transform = pipe(trim, toUpper, exclaim);
console.log(transform(' hello ')); // 'HELLO!'
4.4. compose
Сигнатура: function compose<A, B, C>(f2: (b: B) => C, f1: (a: A) => B): (a: A) => C (и перегрузки)
Описание: Аналог pipe, но выполняется справа налево. Полезно для математических композиций.
import { compose } from '@tool-belt/ts-toolkit';
const addTwo = (x: number) => x + 2;
const multiplyByThree = (x: number) => x * 3;
const addThenMultiply = compose(multiplyByThree, addTwo);
console.log(addThenMultiply(5)); // 21 (сначала addTwo, потом multiplyByThree)
4.5. isObject
Сигнатура: function isObject(value: unknown): value is Record<string, unknown>
Описание: Проверяет, является ли значение объектом (но не null, не массивом и не функцией). Возвращает type guard для TypeScript.
import { isObject } from '@tool-belt/ts-toolkit';
console.log(isObject({})); // true
console.log(isObject([])); // false
console.log(isObject(null)); // false
console.log(isObject(() => {})); // false
4.6. isPlainObject
Сигнатура: function isPlainObject(value: unknown): value is Record<string, unknown>
Описание: Проверяет, является ли значение "простым" объектом (созданным через {} или new Object()). Отличается от isObject тем, что исключает экземпляры классов.
import { isPlainObject } from '@tool-belt/ts-toolkit';
class MyClass {}
console.log(isPlainObject({})); // true
console.log(isPlainObject(new MyClass())); // false
4.7. merge
Сигнатура: function merge<T extends Record<string, unknown>[]>(...objects: T): UnionToIntersection<T[number]>
Описание: Выполняет глубокое слияние нескольких объектов. Последующие объекты перезаписывают предыдущие. Возвращает пересечение типов всех аргументов.
import { merge } from '@tool-belt/ts-toolkit';
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { d: 3 }, e: 4 };
const merged = merge(obj1, obj2);
// Результат: { a: 1, b: { c: 2, d: 3 }, e: 4 }
4.8. pick
Сигнатура: function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>
Описание: Создаёт новый объект, содержащий только указанные ключи из исходного объекта.
import { pick } from '@tool-belt/ts-toolkit';
const user = { id: 1, name: 'Charlie', email: 'charlie@example.com', password: 'secret' };
const safeUser = pick(user, ['id', 'name', 'email']);
// Результат: { id: 1, name: 'Charlie', email: 'charlie@example.com' }
4.9. omit
Сигнатура: function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>
Описание: Создаёт новый объект, исключая указанные ключи из исходного объекта. Противоположность pick.
import { omit } from '@tool-belt/ts-toolkit';
const user = { id: 1, name: 'Diana', email: 'diana@example.com', password: 'secret' };
const publicUser = omit(user, ['password']);
// Результат: { id: 1, name: 'Diana', email: 'diana@example.com' }
4.10. debounce
Сигнатура: function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void
Описание: Создаёт debounced-версию функции, которая откладывает её выполнение до истечения указанного времени после последнего вызова.
import { debounce } from '@tool-belt/ts-toolkit';
const save = (text: string) => console.log('Saving:', text);
const debouncedSave = debounce(save, 500);
debouncedSave('draft 1');
debouncedSave('draft 2');
// Через 500 мс будет вызвано только: 'Saving: draft 2'
4.11. throttle
Сигнатура: function throttle<T extends (...args: any[]) => any>(fn: T, limit: number): (...args: Parameters<T>) => void
Описание: Создаёт throttled-версию функции, которая гарантирует, что она будет вызываться не чаще одного раза за указанный интервал времени.
import { throttle } from '@tool-belt/ts-toolkit';
const logScroll = () => console.log('Scrolled');
const throttledScroll = throttle(logScroll, 200);
window.addEventListener('scroll', throttledScroll);
// Функция будет вызываться максимум раз в 200 мс
4.12. memoize
Сигнатура: function memoize<T extends (...args: any[]) => any>(fn: T, resolver?: (...args: Parameters<T>) => string): T
Описание: Мемоизирует функцию, кэшируя результаты её вызовов на основе аргументов. Опционально можно указать собственную функцию для генерации ключа кэша.
import { memoize } from '@tool-belt/ts-toolkit';
const expensiveCalc = (n: number) => {
console.log('Computing...');
return n * 2;
};
const memoizedCalc = memoize(expensiveCalc);
console.log(memoizedCalc(5)); // 'Computing...' 10
console.log(memoizedCalc(5)); // 10 (без повторного вычисления)
5. Реальный практический пример
Рассмотрим пример создания сервиса для управления пользователями с использованием ts-toolkit. Этот пример демонстрирует комбинирование нескольких функций библиотеки для построения надёжного и типизированного API.
import {
deepClone,
safeGet,
merge,
pick,
omit,
pipe,
isObject,
isPlainObject
} from '@tool-belt/ts-toolkit';
// Определяем типы данных
interface Address {
street: string;
city: string;
zipCode: string;
}
interface User {
id: number;
name: string;
email: string;
password: string;
address: Address;
createdAt: Date;
roles: string[];
}
interface UserPublic {
id: number;
name: string;
email: string;
address: Address;
roles: string[];
}
// Класс-сервис для управления пользователями
class UserService {
private users: Map = new Map();
// Добавление пользователя с глубоким копированием
addUser(user: User): void {
// Создаём глубокую копию, чтобы избежать мутаций извне
const userCopy = deepClone(user);
this.users.set(user.id, userCopy);
}
// Получение пользователя с безопасной навигацией
getUser(id: number): User | undefined {
return safeGet(this.users, id);
}
// Обновление пользователя с частичным слиянием
updateUser(id: number, partial: Partial): User | undefined {
const existing = this.users.get(id);
if (!existing) return undefined;
// Глубокое слияние существующих данных с изменениями
const updated = merge(deepClone(existing), partial);
this.users.set(id, updated as User);
return updated as User;
}
// Получение публичных данных пользователя (без пароля)
getUserPublic(id: number): UserPublic | undefined {
const user = this.users.get(id);
if (!user) return undefined;
// Исключаем пароль и дату создания
return omit(user, ['password', 'createdAt']);
}
// Получение только указанных полей
getUserFields(id: number, fields: (keyof User)[]): Partial | undefined {
const user = this.users.get(id);
if (!user) return undefined;
return pick(user, fields);
}
// Валидация пользователя с использованием type guards Также в библиотеке
Работа с CSV в Swift (iOS/macOS) — библиотеки и примеры кода
league/csv для PHP: полное руководство с примерами
Работа с Kafka в JavaScript (Node.js): библиотека node-rdkafka