TypeScript
Для чего модуль
Использовать TypeScript как инженерный инструмент качества: уменьшать классы ошибок, ускорять рефакторинг и делать контракты системы прозрачными.
Результат после прохождения
- Вы строите доменные типы, а не «просто аннотации для компилятора».
- Вы уверенно применяете narrowing, discriminated unions и generics.
- Вы типизируете границы системы (UI/API/domain) с учетом runtime-валидации.
- Вы управляете строгими настройками TS в больших проектах без «type debt».
Термины и аббревиатуры
| Термин | Коротко |
|---|---|
Union | Тип из нескольких вариантов |
Narrowing | Уточнение типа проверками |
Generic | Тип-параметр |
DTO | Контракт данных на границе |
unknown | Безопасный неизвестный тип |
Фокус по грейдам
Junior: понимать базовые механики и объяснять их простыми примерами.Middle: применять тему в продуктовых сценариях с учетом рисков и ограничений.Senior: управлять архитектурными trade-offs, метриками и эволюцией решения.
Как работать с модулем
- Все примеры прогоняйте в
strict-режиме. - На каждый урок делайте мини-рефакторинг существующего кода.
- Для каждой типовой конструкции фиксируйте: какой класс ошибок она предотвращает и какой ценой.
Программа модуля
Урок 1. Базовая тип-модель
Цель: описывать доменную модель так, чтобы типы отражали реальные состояния системы.
Union, intersection, literal types
- Union описывает варианты состояния.
- Intersection собирает составные контракты.
- Literal types фиксируют допустимые значения.
Discriminated unions и exhaustiveness
type LoadState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
function renderState(s: LoadState<string[]>) {
switch (s.status) {
case 'idle':
case 'loading':
case 'success':
case 'error':
return s.status;
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}
Где ломается в проде
- Моделируют «частично известный объект» через
any. - Не описывают переходы состояния и получают impossible states.
- Выключают строгие проверки ради скорости.
Мини-задача (обязательная)
Смоделируйте состояние async-флоу через discriminated union, добавьте exhaustiveness check и покажите, какую ошибку это предотвращает.
Что спросит интервьюер:
как never помогает ловить ошибки в switch.
Критерий готовности по уроку: вы можете выразить типами все допустимые состояния фичи и исключить невозможные комбинации.
Урок 2. Generics и переиспользование
Цель: писать переиспользуемые типобезопасные API без перегрузки сложностью.
Где generics действительно нужны
- Обобщенные контейнеры/репозитории.
- Типобезопасные utility функции.
- Переиспользуемые UI-абстракции (таблицы, формы, query hooks).
Ограничения и вывод типов
- Используйте
extendsдля ограничений. - Не прячьте доменную семантику за слишком абстрактными
T/U/V. - Проверяйте, что infer работает как ожидается в consumer-коде.
function byId<T extends { id: string }>(items: T[]): Record<string, T> {
return Object.fromEntries(items.map((i) => [i.id, i]));
}
Utility types в реальной жизни
Pick/Omit/Partial/Required/Readonly/Record полезны,
когда отражают контракт, а не используются как «быстрый костыль».
Где ломается в проде
- Generic API становится сложнее, чем конкретная реализация.
- Типы «протекают» через
as unknown as. - Повсеместный
Partial<T>размывает инварианты.
Мини-задача (обязательная)
Реализуйте generic-тип и функцию пагинации: данные + мета + безопасный контракт ошибок.
Что спросит интервьюер: когда generic делает API слишком сложным и как упростить.
Критерий готовности по уроку: вы пишете generic-решения, которые повышают надежность и остаются читаемыми для команды.
Урок 3. Типизация границ системы
Цель: сделать границы UI/API/domain устойчивыми к изменениям и ошибкам интеграции.
Compile-time vs runtime
TypeScript не валидирует входные данные в runtime. Если API вернул неожиданный payload, compile-time не спасет.
DTO и валидация входа
- Определите входной DTO.
- Проверьте payload на runtime boundary.
- Только после этого маппите в внутренние доменные типы.
// идея: сначала parse/validate, потом используем тип как trusted
Типизация ошибок и событий
- Ошибки должны иметь машинно-читаемый
code. - UI состояния и domain events описываются типами отдельно.
- Переходы между состояниями — явные и проверяемые.
Где ломается в проде
- «Доверяют» внешнему API без runtime-check.
- Смешивают transport и domain типы.
- Type-safe facade внутри, но
anyна внешней границе.
Мини-задача (обязательная)
Опишите типобезопасный flow: API response -> runtime validation -> domain mapping -> UI state. Добавьте один пример неверного payload и ожидаемое поведение.
Что спросит интервьюер: почему compile-time типизация не заменяет runtime валидацию.
Критерий готовности по уроку: вы можете объяснить и показать устойчивую типизацию границ, которая переживает изменения внешнего контракта.
Урок 4. TS в большом проекте
Цель: управлять TypeScript как частью инженерного процесса команды.
Конфигурация и strictness
strict,noUncheckedIndexedAccess,exactOptionalPropertyTypes(по контексту проекта).- Запрет «тихих» путей деградации (
any, неявные assertions). - Стратегия миграции legacy-кода без freeze разработки.
Типовой техдолг
- Локальные
anyразмножаются и размывают контракты. ts-ignoreостается без срока удаления.- Нет ownership за типовые правила и code review стандарты.
Процесс качества
- Type coverage/линты как quality gate.
- Checklist в PR: какие типовые риски добавлены/сняты.
- Документация типовых паттернов команды.
Где ломается в проде
- Обновление библиотеки рушит типовую совместимость.
- Рефакторинг проходит компиляцию, но ломает runtime boundary.
- Нет механики отслеживания type debt.
Мини-задача (обязательная)
Сделайте план миграции legacy-модуля к strict TS на 3 итерации: quick wins, среднесрочные фиксы, долгосрочные стандарты.
Что спросит интервьюер: как внедрять строгий TS в проект без остановки delivery.
Критерий готовности по уроку: вы можете управлять TS-качеством проекта как системной инженерной задачей.
Практика
- Разберите TypeScript вопросы и релевантные темы Senior Frontend.
- Реализуйте
LoadStateмодель с exhaustiveness и UI-рендерингом. - Сделайте generic pagination API + типизированный error contract.
- Прототипируйте boundary-layer с runtime validation и маппингом в domain типы.
- Подготовьте план strict-migration для одного legacy-модуля.
- Соберите типобезопасный API boundary (DTO + runtime check) в Песочнице.
Связь с треками и вопросами
- Треки: Middle трек, Senior трек.
- Вопросы: Middle JavaScript и React, Senior Frontend.
- Повторение: 3-5 вопросов без подсказок -> сверка с модулем -> повтор через 24 часа.
Критерий готовности
Вы используете TS не как «украшение», а как механизм снижения ошибок и ускорения безопасных изменений.
Артефакты после модуля
- Набор типовых паттернов (state, errors, events, DTO mapping).
- Шаблон generic pagination + error model.
- Документ миграции legacy к strict TS.
- Набор из 6 сильных interview-ответов по TypeScript.