Перейти к содержимому
Фёдор Башинский

Полезные приёмы TypeScript для повседневной разработки

2 min read
Обложка статьи «Полезные приёмы TypeScript для повседневной разработки»

TypeScript даёт не только автодополнение, но и возможность закладывать инварианты прямо в систему типов. Вот несколько приёмов, которыми я пользуюсь почти в каждом проекте.

Discriminated unions вместо опциональных полей

Вместо набора опциональных полей опишите состояние через дискриминирующее поле:

type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: string };

Компилятор сам заставит проверить status, прежде чем обратиться к data или error.

Утилитные типы

Pick, Omit, Partial, Record избавляют от дублирования типов:

type UserPreview = Pick<User, "id" | "name" | "avatarUrl">;
type UpdateUserInput = Partial<Omit<User, "id">>;

satisfies вместо явной аннотации

Оператор satisfies проверяет соответствие типу, но сохраняет более узкий выведенный тип:

const routes = {
  home: "/",
  blog: "/blog",
} satisfies Record<string, `/${string}`>;

Zod для границ приложения

Типы TypeScript исчезают во время выполнения. На границах приложения — формы, API-роуты, ответы внешних сервисов — данные нужно валидировать в рантайме. Схема Zod одновременно валидирует значение и выводит из него TypeScript-тип:

const ContactSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  message: z.string().min(10),
});
 
type ContactInput = z.infer<typeof ContactSchema>;

Итог

Строгая типизация — это не бюрократия, а способ переложить часть проверок на компилятор ещё до запуска кода. Чем раньше найдена ошибка, тем дешевле её исправить.