Как перестать писать Any и полюбить магию типов в TypeScript

13 Jul, 2026
17,301
🔱 724
👥 50

Знакомая ситуация: вы описываете интерфейс для API-ответа, и вдруг понимаете, что вам нужно то же самое, но чтобы все поля были обязательными, а одно конкретное — удалено. Или вам нужно сделать объект глубоко неизменяемым (deep readonly), а стандартный Readonly<T> из TypeScript работает только на первом уровне вложенности. В такие моменты начинаешь чувствовать, что встроенных Utility Types катастрофически не хватает.

Обычно разработчики либо начинают городить свои «велосипеды» с использованием infer и рекурсии, либо сдаются и пишут any, обещая себе вернуться и поправить это позже (спойлер: нет). Но есть решение изящнее.

Речь пойдет о проекте Type-fest от Синдре Сорхуса. Это огромная коллекция «сахарных» типов, которые, по-хорошему, должны быть в TypeScript из коробки.

type-fest

Что это такое и зачем оно в вашем package.json

Если коротко, это библиотека типов. В ней нет исполняемого JavaScript-кода, она не весит ничего в бандле и нужна только на этапе разработки. Синдре Сорхус, автор сотен популярных npm-пакетов, собрал здесь всё то, что ему самому приходилось копипастить из проекта в проект.

Type-fest решает одну простую задачу: делает систему типов TypeScript по-настоящему гибкой. Вместо того чтобы тратить полчаса на написание сложного типа, который вытаскивает ключи из объединения объектов, вы просто импортируете готовое решение.

Реклама

Четыре типа, которые спасают рабочий день

В библиотеке десятки утилит, но я выделил те, которые пригождаются почти в каждом проекте.

Глубокая модификация объектов

Стандартные Partial<T> или Readonly<T> работают поверхностно. Если внутри объекта есть другой объект, он останется нетронутым. В Type-fest есть PartialDeep, ReadonlyDeep и RequiredDeep.

import type {ReadonlyDeep} from 'type-fest';

interface UserConfig {
    api: {
        endpoint: string;
        timeout: number;
    };
}

const config: ReadonlyDeep<UserConfig> = {
    api: { endpoint: 'https://api.com', timeout: 5000 }
};

// TypeScript выдаст ошибку: свойство endpoint только для чтения
config.api.endpoint = 'https://other.com'; 

Работа с JSON

Часто мы пишем any для данных, которые приходят из JSON.parse(). Тип JsonValue из библиотеки строго описывает всё, что может быть валидным JSON: строки, числа, булевы значения, null, массивы и объекты из этих же типов. А Jsonify поможет превратить сложный класс или объект с методами в то, как он будет выглядеть после JSON.stringify().

Умное объединение типов

Тип Merge<A, B> позволяет объединить два интерфейса так, чтобы поля из второго перекрывали поля из первого. Это гораздо удобнее, чем пытаться использовать пересечение &, которое часто ведет к конфликтам типов.

Манипуляции со строками на уровне типов

TypeScript умеет работать с шаблонными литералами, но Type-fest выжимает из этого максимум. Нужно превратить все ключи объекта из snake_case в camelCase? Для этого есть CamelCasedPropertiesDeep. Это незаменимо, когда бэкенд отдает данные в одном стиле, а во фронтенде принят другой.

import type {CamelCasedProperties} from 'type-fest';

interface RawUser {
    first_name: string;
    last_name: string;
}

type User = CamelCasedProperties<RawUser>;
// Результат: { firstName: string; lastName: string; }

Техническая сторона вопроса

Библиотека требует TypeScript версии 5.9 или выше и включенного режима strict: true в вашем tsconfig.json. Это логично: современные манипуляции с типами опираются на фичи последних версий языка.

Интересно, что авторы не заставляют вас тащить весь пакет в зависимости. Если вам нужен всего один тип, в README прямо сказано: «Просто скопируйте нужный код к себе». Лицензия MIT это позволяет. Но на практике удобнее держать пакет в devDependencies, чтобы получать обновления и исправления багов.

Когда стоит использовать Type-fest

Я часто вижу этот пакет в крупных open-source проектах. Он идеально подходит, если вы:

  1. Пишете библиотеку и хотите, чтобы у пользователей были идеальные автодополнения в IDE.
  2. Работаете со сложными структурами данных, которые часто трансформируются.
  3. Устали от бесконечных Omit<T, 'key'> & { key: NewType }.

Проект живой, у него огромное сообщество (более 17 тысяч звезд на GitHub) и очень строгий отбор новых типов. Синдре часто отклоняет предложения, если они решают слишком специфичные задачи или если их можно реализовать парой строк стандартного кода. Это гарантирует, что библиотека не превратится в свалку всего подряд.

Type-fest — это не тот инструмент, который меняет архитектуру приложения, но это тот инструмент, который делает код чище, а разработку — приятнее. Он избавляет от необходимости гуглить «typescript how to pick keys by value type» и позволяет сфокусироваться на логике приложения, а не на борьбе с компилятором.

Если вы до сих пор используете Record<string, any> для описания объектов, попробуйте заменить его на UnknownRecord или JsonValue из этой библиотеки. Это маленький шаг, с которого начинается по-настоящему типобезопасный код.

🍪 Мы используем файлы cookie и сервис аналитики Яндекс.Метрика, чтобы сайт работал лучше. Продолжая пользоваться devtrends.ru, вы соглашаетесь с обработкой данных согласно Политике конфиденциальности.