Перейти к основному содержимому

TypeScript и React

Разработчику Архитектору

Дальше: TypeScript и Node.js · События · Асинхронность · Справочник — интеграция с фреймворками


React описывает UI как функции от состояния. TypeScript добавляет контракты — какие props обязательны, что лежит в state, какие поля пришли с API. Это снижает поломки при рефакторинге компонентов и согласует фронт с backend-DTO. Удобный порядок — типы и типизация, асинхронность, эта статья, обработка ошибок.

Курс по React: справочник 271. Старт проекта — Первая программа на TypeScript + npm create vite@latest -- --template react-ts.


Интеграция с фреймворками (обзор)

Эта статья — React; общая картина Справочник — интеграция с фреймворками:

СтекШаблон / типыУчебный материал
Reactreact-ts, @types/reactэта статья, Справочник по React
Vuevue-tsc, defineComponentСправочник по Vue.js
Angularвстроенный TS, strict templatesСправочник по Angular
Node.js@types/node, DTO APITypeScript и Node.js

Общие принципы для всех — типизировать props/inputs, ответы API, состояния загрузки (discriminated union) — Типы данных и типизация в TypeScript, Асинхронное программирование в TypeScript.


TypeScript в React-проектах

Без TSС TS
Опечатка в props — в runtimeОшибка при сборке / в IDE
Неверное поле от APIПодсветка при доступе
Рефакторинг prop nameFind references + компилятор
useState(null) — любой типuseState<User | null>(null)

TypeScript не заменяет тесты и не валидирует JSON с сервера сам — для API нужны guards или Zod — Рекомендации по разработке на TypeScript, Асинхронное программирование в TypeScript.


Старт — Vite + React + TS

npm create vite@latest my-app -- --template react-ts
cd my-app && npm install && npm run dev

Разбор:

  • Шаблон даёт tsconfig.app.json, типы React, ESLint.
  • Сборку делает Vite; типы проверяют tsc -bpackage.json часто в скрипте build перед vite build). Отдельно для CI удобно tsc --noEmit.

Структура типов:

src/
├── components/
├── types/
│ └── api.ts # DTO и LoadState
└── App.tsx

Компонент и props

Код ITЗагрузка примера кода…

Разбор:

  • children?: React.ReactNode — текст, элемент, массив, null.
  • Не обязательно React.FC — явный тип props у функции читаемее; в Справочник по TypeScript описаны варианты.

Расширение props нативного элемента

type ButtonProps = {
label: string;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

function Button({ label, ...rest }: ButtonProps) {
return <button type="button" {...rest}>{label}</button>;
}

Разбор:

  • ...rest пробрасывает onClick, disabled, className с проверкой типов.

Типизация UI-компонентов

Сниппеты из Uiverse / Galaxy — это HTML+CSS. В React/Vue те же блоки оформляют props и discriminated unions, чтобы варианты кнопки, поля и переключателя не смешивались.

Код ITЗагрузка примера кода…

Поле ввода с ошибкой с сервера:

Код ITЗагрузка примера кода…

Тип переключателя (checkbox, radio или switch) — union, чтобы не перепутать API:

type ToggleControl =
| { kind: 'checkbox'; checked: boolean; onChange: (v: boolean) => void; label: string }
| { kind: 'radio'; name: string; value: string; checked: boolean; label: string }
| { kind: 'switch'; checked: boolean; onChange: (v: boolean) => void; label: string };

Стили из Типовых элементов интерфейса подключают через CSS Modules или глобальный класс; логика остаётся в TypeScript.

Готовые файлы Button.tsx, ProfileCard.tsx, ThemeSwitch.tsx, Tooltip.tsx с разбором — React — UI на TypeScript.


useState с явным типом

type User = { id: string; name: string };

function UserBadge() {
const [user, setUser] = React.useState<User | null>(null);

if (!user) return <p>Не авторизован</p>;
return <span>{user.name}</span>;
}

Разбор:

  • useState(null) без generic в strict даёт только null — укажите User | null.
  • Обновление: setUser({ id: "1", name: "Ann" }) проверяется на соответствие User.

LoadState в компоненте

Тот же discriminated union, что в Асинхронное программирование в TypeScript:

Код ITЗагрузка примера кода…

Разбор:

  • cancelled в cleanup — защита от setState после размонтирования.
  • switch по status сужает тип без лишних флагов isLoading.

useReducer с типизированными actions

Когда состояние и переходы сложнее, чем один setState, удобен discriminated union для действий:

Код ITЗагрузка примера кода…

Разбор:

  • CartAction — union с полем type; в ветке switch доступны только нужные поля (sku в add).
  • dispatch({ type: "addd", sku: "x" }) — ошибка компиляции (опечатка в type).
  • Тот же приём, что для LoadState, но для событий UIПаттерны в TypeScript.

ComponentProps — переиспользование props

Не дублируйте поля нативного элемента — возьмите тип у готового компонента или HTML-тега:

Код ITЗагрузка примера кода…

Разбор:

  • ComponentProps<"button"> — все допустимые props <button> с типами событий.
  • Pick оставляет в публичном API только нужные поля обёртки.

События форм

Код ITЗагрузка примера кода…

Тип событияЭлемент
ChangeEvent<HTMLInputElement><input>
ChangeEvent<HTMLSelectElement><select>
FormEvent<HTMLFormElement><form onSubmit>
MouseEvent<HTMLButtonElement><button onClick>

Подробнее DOM — События и обработка событий в TypeScript.


useRef и DOM

function FocusInput() {
const ref = React.useRef<HTMLInputElement>(null);

const focus = () => ref.current?.focus();

return (
<>
<input ref={ref} />
<button type="button" onClick={focus}>Фокус</button>
</>
);
}

Разбор:

  • ref.current имеет тип HTMLInputElement | null — нужен optional chaining.

Контекст с типом

Код ITЗагрузка примера кода…

Разбор:

  • null по умолчанию заставляет проверять провайдер в хуке.
  • Альтернатива — значение по умолчанию в createContext (менее строго).

Слой типов API

src/types/api.ts:

export type UserDto = { id: string; name: string; email: string };
export type CreateUserBody = Pick<UserDto, "name" | "email">;

Компонент импортирует только типы:


import type { UserDto } from "../types/api.js";

Разбор:


Дженерики в компонентах

Код ITЗагрузка примера кода…

См. Дженерики в TypeScript.


Частые ошибки

ОшибкаЧто делать
props: anyявный type Props
as User на json()unknown + guard
Разрозненные loading/errorLoadState union
Забыли children в типеReact.ReactNode
React.FC с implicit childrenявные props (стиль команды)
Типы API в каждом файлеtypes/api.ts

Практика

  1. Создайте react-ts проект и компонент Card с typed props.
  2. Реализуйте Profile с LoadState и isUser guard.
  3. Форма с onSubmit и ChangeEvent<HTMLInputElement>.
  4. Вынесите UserDto в types/api.ts, используйте import type.
  5. Добавьте generic List<T> для массива задач.
  6. Перепишите локальное состояние корзины на useReducer с union actions.

Смежные статьи