TypeScript с React

Почему стоит использовать TypeScript с React?

TypeScript расширяет возможности React следующим образом:

  • Статическая проверка типов для пропсов, состояния и контекста
  • Подсказки автозавершения и рефакторинга в IDE
  • Раннее выявление ошибок во время разработки
Примечание: Данный раздел предполагает наличие базовых знаний React.

Если вы новичок в React, сначала ознакомьтесь с нашим учебником React.


Начало работы

Создайте новое приложение React + TypeScript с помощью Vite:

Пример


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

Ваш файл tsconfig.json должен включать следующие рекомендуемые настройки компилятора:

Пример


{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Node",
    "jsx": "react-jsx",
    "strict": true,
    "skipLibCheck": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src"]
}

Примечание: Оставляйте настройку strict включенной для лучшей защиты типов.

Показанные опции хорошо работают с Vite и Create React App.


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

Определите пропсы с помощью TypeScript и используйте их в функциональном компоненте:

Пример


// Greeting.tsx
type GreetingProps = {
  name: string;
  age?: number;
};

export function Greeting({ name, age }: GreetingProps) {
  return (
    <div>
      <h2>Привет, {name}!</h2>
      {age !== undefined && <p>Вам {age} лет</p>}
    </div>
  );
}


Распространённые шаблоны

Безопасные события

Типизируйте обработчики событий для ввода и кнопок:

Пример


// Изменение ввода
function NameInput() {
  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    console.log(e.target.value);
  }
  return <input onChange={handleChange} />;
}

// Нажатие кнопки
function SaveButton() {
  function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
    e.preventDefault();
  }
  return <button onClick={handleClick}>Сохранить</button>;
}

Типизация состояния с помощью useState

Используйте явные типы для чисел, объединений и нулевых значений:

Пример


const [count, setCount] = React.useState<number>(0);
const [status, setStatus] = React.useState<'idle' | 'loading' | 'error'>('idle');

type User = { id: string; name: string };
const [user, setUser] = React.useState<User | null>(null);

useRef с элементами DOM

Типизируйте ссылки на элементы DOM для безопасного доступа к свойствам:

Пример


function FocusInput() {
  const inputRef = React.useRef<HTMLInputElement>(null);
  return <input ref={inputRef} onFocus={() => inputRef.current?.select()} />;
}

Типизация дочерних элементов

Принимайте дочерние элементы с типом React.ReactNode:

Пример


type CardProps = { title: string; children?: React.ReactNode };
function Card({ title, children }: CardProps) {
  return (
    <div>
      <h2>{title}</h2>
      {children}
    </div>
  );
}

Вспомогательные функции для запросов с обобщенными типами

Используйте обобщения для типизации ответов API:

Пример


async function fetchJson<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Ошибка сети');
  return res.json() as Promise<T>;
}

// Использование внутри асинхронной функции/события компонента
async function loadPosts() {
  type Post = { id: number; title: string };
  const posts = await fetchJson<Post[]>("/api/posts");
  console.log(posts);
}

Минимальный контекст и пользовательский хук

Предоставляйте небольшой, типизированный контекст и вспомогательный хук:

Пример


type Theme = 'light' | 'dark';
const ThemeContext = React.createContext<{ theme: Theme; toggle(): void } | null>(null);

function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = React.useState<Theme>('light');
  const value = { theme, toggle: () => setTheme(t => (t === 'light' ? 'dark' : 'light')) };
  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}

function useTheme() {
  const ctx = React.useContext(ThemeContext);
  if (!ctx) throw new Error('useTheme должен использоваться внутри ThemeProvider');
  return ctx;
}

Типы Vite для TypeScript: добавьте глобальные типы Vite, чтобы избежать отсутствия определений.

Пример


// src/vite-env.d.ts
/// <reference types="vite/client" />

Альтернативно, добавьте в tsconfig.json:

Пример


{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

О React.FC: Используйте непосредственно типизированные функциональные компоненты.

React.FC является необязательным; он неявно добавляет children, но не обязателен.

Необязательные baseUrl и paths: Эти параметры упрощают импорт, если поддерживаются вашим пакетом сборки.

Пример


{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

Конфигурируйте только в том случае, если ваше окружение (например, Vite, tsconfig-paths) настроено для псевдонимов путей.