Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Современный JavaScript и TypeScript для React
modern_js_ts

Современный JavaScript и TypeScript для React

Короткий рабочий мост: модули, замыкания, неизменяемые преобразования, Promise, strict TypeScript, unknown, narrowing и discriminated unions.

Открыть лабораториюv0.1.0Запускается локально из публичного репозитория

Современный JavaScript и TypeScript для React

Повторяем только то, что немедленно понадобится в компонентах, и сразу закрепляем это строгими типами.

#Результат урока

Вы сможете принять неизвестные данные, проверить их на границе, преобразовать без мутаций и описать конечное множество состояний так, чтобы компилятор находил пропущенные ветви.

До начала достаточно уметь читать базовый JavaScript: переменные, функции, массивы и объекты. В конце должен появиться не конспект, а типизированный модуль нормализации данных с асинхронной границей и тестируемыми чистыми функциями.

#1. Значения, ссылки и неизменяемые преобразования

Примитивы копируются как значения, а объекты и массивы передаются через ссылки на один объект. Spread создаёт только поверхностную копию: вложенный массив остаётся общим. React опирается на замену ссылок при обновлении состояния, поэтому входные данные и прежний state нельзя изменять. Используйте map, filter, object spread и точечное копирование каждого изменяемого уровня.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

type Item = { id: string; meta: { selected: boolean } }; function select(items: readonly Item[], id: string): Item[] { return items.map(item => item.id === id ? { ...item, meta: { ...item.meta, selected: true } } : item, ); }

readonly защищает контракт на уровне типов, а копирование meta не даёт новой версии разделить изменяемый вложенный объект со старой.

Рабочая проверка. Докажите тестом, что результат изменился, а исходный массив и вложенный meta остались прежними.

Типичная поломка. const copy = [...items]; copy[0].meta.selected = true меняет meta и в исходном массиве.

#2. Функции, замыкания и ES-модули

Функция замыкает переменные из лексической области, а не их исторические значения. Обработчик может видеть снимок конкретного рендера, потому что каждый вызов компонента создаёт новую область. ES-модуль вычисляется один раз на экземпляр module graph, поэтому изменяемая переменная верхнего уровня становится общей для всех потребителей и плохо подходит для пользовательского состояния.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

export function createSequence(start = 0) { let current = start; return () => { current += 1; return current; }; }

Состояние принадлежит экземпляру фабрики. Если current вынести на уровень модуля, независимые потребители неожиданно начнут делить счётчик.

Рабочая проверка. Создайте две последовательности и проверьте, что вызовы одной не меняют другую.

Типичная поломка. Хранить текущего пользователя или состояние формы в изменяемой переменной модуля.

#3. Promise, event loop и отмена результата

async-функция всегда возвращает Promise. Продолжение после await выполняется как microtask, а не блокирует поток. Сам Promise не отменяется от потери интереса потребителя: нужно передать AbortSignal поддерживающему API либо игнорировать устаревший результат по явному поколению запроса. Ошибка до await и отклонение Promise сходятся в rejected Promise.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

async function loadProduct(id: string, signal: AbortSignal) { const response = await fetch('/api/products/' + encodeURIComponent(id), { signal }); if (!response.ok) throw new Error('http_' + response.status); return (await response.json()) as unknown; }

fetch принимает AbortSignal, но JSON всё равно остаётся unknown: успешный HTTP-статус не доказывает форму тела.

Рабочая проверка. Проверьте отклонение при HTTP 500 и отдельный путь AbortError без показа ложного сообщения об отказе.

Типичная поломка. Считать, что await блокирует браузер или автоматически отменяет предыдущий запрос.

#4. unknown и проверка внешней границы

Данные сети, localStorage и postMessage не становятся надёжными от generic-параметра клиента. Тип unknown запрещает использовать значение до доказательства. Type guard должен проверить каждое поле, на которое опирается доменная модель; typeof null === "object", поэтому null обрабатывается отдельно.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

type Product = { id: string; price: number }; function isProduct(value: unknown): value is Product { if (typeof value !== 'object' || value === null) return false; const row = value as Record<string, unknown>; return typeof row.id === 'string' && Number.isFinite(row.price); }

Локальное приведение к Record<string, unknown> разрешает безопасно инспектировать ключи; наружу выходит только значение, прошедшее проверки.

Рабочая проверка. Добавьте случаи null, массива, NaN, пропущенного id и лишних полей.

Типичная поломка. Написать const product = await response.json() as Product и считать контракт проверенным.

#5. Narrowing и discriminated unions

Проверки typeof, in, равенство литералу и пользовательские guards сужают union по control flow. Для UI особенно полезен discriminated union: состояния idle, loading, success, failure получают разные обязательные поля. Так невозможно одновременно иметь loading: true, старую ошибку и отсутствующие данные без явного решения.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

type LoadState<T> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'failure'; error: string }; function message<T>(state: LoadState<T>): string { switch (state.status) { case 'idle': return 'Не запускалось'; case 'loading': return 'Загрузка'; case 'success': return 'Готово'; case 'failure': return state.error; } }

Дискриминант status связывает поля с состоянием. Несколько независимых boolean-флагов такой связи не выражают.

Рабочая проверка. Добавьте состояние refreshing со старыми данными и добейтесь ошибки компилятора во всех неполных switch.

Типичная поломка. Моделировать асинхронный UI тремя независимыми полями data?, error?, loading.

#6. Generics, satisfies и сохранение вывода типов

Generic связывает несколько позиций типа, а не служит украшением функции. satisfies проверяет совместимость выражения с контрактом, сохраняя более точный выведенный тип самого выражения. В отличие от as, он не просит компилятор поверить разработчику и полезен для реестров маршрутов, вариантов и конфигураций.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

type Route = { path: string; auth: boolean }; const routes = { home: { path: '/', auth: false }, account: { path: '/account', auth: true }, } satisfies Record<string, Route>; type RouteName = keyof typeof routes;

RouteName остаётся union конкретных ключей, а каждое значение одновременно проверяется по Route.

Рабочая проверка. Добавьте маршрут без auth и сравните диагностику satisfies, явной аннотации и assertion.

Типичная поломка. Использовать as Record<string, Route> и скрыть ошибку конфигурации.

#7. Типы функций и обработчиков

Функциональный контракт включает параметры, возвращаемое значение и допустимость Promise. Тип () => void означает, что вызывающий игнорирует возвращаемое значение; он не превращает async-ошибки в обработанные. Для доменных команд полезнее явный Promise<Result> или синхронный result union. При включённом strictFunctionTypes нельзя безопасно подменять обработчик функцией, принимающей более узкий вход.

Рабочая проверка. Смоделируйте команду сохранения как () => Promise<SaveResult> и заставьте вызывающий код разобрать отказ.

Типичная поломка. Передать async callback в API, которое ожидает void, и потерять rejected Promise.

#8. Исчерпывающие переходы и never

never представляет невозможное значение после полного narrowing. Функция assertNever превращает добавление нового варианта union в ошибку компиляции там, где переход не обработан. Это особенно полезно в reducers и отображении конечных автоматов, но вызов должен находиться после корректного исчерпывающего switch.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

function assertNever(value: never): never { throw new Error('Unhandled state: ' + JSON.stringify(value)); }

Runtime-throw остаётся последней защитой от данных, пришедших в обход TypeScript, а параметр never обеспечивает compile-time сигнал.

Рабочая проверка. Добавьте событие reducer и убедитесь, что все переходы, селекторы и представления требуют обновления.

Типичная поломка. Поставить default: return state, скрыв новое событие без поведения.

#Лаборатория

Получите массив неизвестных записей каталога, провалидируйте форму каждой записи, нормализуйте цены и статусы, а затем верните discriminated union LoadResult. Запрещены any, мутация входа и утверждение as Product до runtime-проверки.

Критерий завершения: проверка типов отклоняет незаконные состояния, тесты доказывают неизменность входа и корректную обработку отказа Promise. Сначала зафиксируйте наблюдаемое поведение тестом или измерением, затем меняйте реализацию.

#Источники для сверки

  • TypeScript Handbook
  • TypeScript narrowing
  • MDN JavaScript Guide

Устранение неисправностей

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Инструментарий React-проекта