- Quick Start
- Installation
- Describing the UI
- Adding Interactivity
- Managing State
- Escape Hatches
GET STARTED
LEARN REACT
Використання TypeScript
TypeScript - це популярний спосіб додавання визначень типів до кодових баз JavaScript. Одразу після установки TypeScript підтримує JSX, і ви можете отримати повну веб-підтримку React, додавши @types/react
та @types/react-dom
до вашого проекту.
Установка
Усі фреймворки React продуктивного рівня пропонують підтримку використання TypeScript. Дотримуйтесь інструкцій з встановлення для конкретного фреймворку:
Додавання TypeScript до існуючого React-проекту
Як встановити останню версію визначень типів React:
npm install @types/react @types/react-dom
У вашому tsconfig.json
компіляторі потрібно встановити наступні опції:
dom
має бути включено доlib
(Зауваження: якщо не вказано параметрlib
, за замовчуванням включаєтьсяdom
).jsx
має бути встановлено в одне з допустимих значень.preserve
має бути достатнім для більшості програм. Якщо ви публікуєте бібліотеку, зверніться до документаціїjsx
щодо того, яке значення слід обрати.
Типізація сценарію за допомогою React-компонентів
Кожен файл, що містить JSX, повинен мати розширення .tsx
. Це специфічне для TypeScript розширення, яке повідомляє TypeScript, що цей файл містить JSX.
Написання TypeScript за допомогою React дуже схоже на написання JavaScript за допомогою React. Ключова відмінність при роботі з компонентом полягає в тому, що ви можете надавати типи для пропсів вашого компонента. Ці типи можна використовувати для перевірки коректності та надання вбудованої документації в редакторах.
Взявши MyButton
компонент з посібника Швидкий старт, ми можемо додати тип, що описує заголовок
для кнопки:
function MyButton({ title }: { title: string }) {
return (
<button>{title}</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton title="I'm a button" />
</div>
);
}
import AppTSX from "./App.tsx";
export default App = AppTSX;
Ці пісочниці можуть обробляти код TypeScript, але вони не запускають перевірку типів. Це означає, що ви можете вносити зміни у пісочниці TypeScript для навчання, але ви не отримаєте жодних типових помилок або попереджень. Щоб отримати перевірку типів, ви можете скористатися TypeScript Playground або скористатися більш повнофункціональною онлайн-пісочницею.
Цей вбудований синтаксис є найпростішим способом визначення типів для компонента, хоча, коли ви починаєте описувати декілька полів, він може стати громіздким. Замість цього ви можете використовувати інтерфейс
або тип
для опису пропсів компонента:
interface MyButtonProps {
/** The text to display inside the button */
title: string;
/** Whether the button can be interacted with */
disabled: boolean;
}
function MyButton({ title, disabled }: MyButtonProps) {
return (
<button disabled={disabled}>{title}</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton title="I'm a disabled button" disabled={true}/>
</div>
);
}
import AppTSX from "./App.tsx";
export default App = AppTSX;
Тип, що описує пропси вашого компонента, може бути настільки простим або складним, наскільки вам потрібно, хоча це має бути об'єктний тип, описаний за допомогою type
або interface
. Ви можете дізнатися про те, як TypeScript описує об'єкти у Типи об'єктів, але вас також може зацікавити використання Типів об'єднань для опису реквізиту, який може бути одним з декількох різних типів, а також посібник Створення типів з типів для більш складних випадків використання.
Приклади хуків
Визначення типів з @types/react
включають типи для вбудованих хуків, тому ви можете використовувати їх у своїх компонентах без додаткового налаштування. Їх створено з урахуванням коду, який ви пишете у своєму компоненті, тому ви отримуватимете здебільшого виведені типи, і в ідеалі вам не потрібно буде опрацьовувати деталі надання типів.
Втім, ми можемо розглянути декілька прикладів того, як надавати типи для хуків.
useState
Хук useState
повторно використає значення, передане в якості початкового стану, щоб визначити тип значення. Наприклад:
// Infer the type as "boolean"
const [enabled, setEnabled] = useState(false);
Присвоїть тип boolean
для enabled
, а setEnabled
буде функцією, яка приймає аргумент boolean
, або функцією, яка повертає boolean
. Якщо ви хочете явно вказати тип для стану, ви можете зробити це, надавши аргумент типу виклику useState
:
// Explicitly set the type to "boolean"
const [enabled, setEnabled] = useState<boolean>(false);
У цьому випадку це не дуже корисно, але типовим випадком, коли вам може знадобитися вказати тип, є тип об'єднання. Наприклад, status
тут може бути одним з декількох різних рядків:
type Status = "idle" | "loading" | "success" | "error";
const [status, setStatus] = useState<Status>("idle");
Або, як рекомендовано у Принципах структурування стану, ви можете згрупувати пов'язані стани як об'єкт і описати різні можливості за допомогою типів об'єктів:
type RequestState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success', data: any }
| { status: 'error', error: Error };
const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' });
useReducer
Хук useReducer
є більш складним хуком, який приймає функцію-відповідник та початковий стан. Типи для функції-редуктора виводяться з початкового стану. Ви можете за бажанням надати аргумент типу до виклику useReducer
, щоб отримати тип для стану, але часто краще встановити тип для початкового стану замість цього:
import {useReducer} from 'react';
interface State {
count: number
};
type CounterAction =
| { type: "reset" }
| { type: "setCount"; value: State["count"] }
const initialState: State = { count: 0 };
function stateReducer(state: State, action: CounterAction): State {
switch (action.type) {
case "reset":
return initialState;
case "setCount":
return { ...state, count: action.value };
default:
throw new Error("Unknown action");
}
}
export default function App() {
const [state, dispatch] = useReducer(stateReducer, initialState);
const addFive = () => dispatch({ type: "setCount", value: state.count + 5 });
const reset = () => dispatch({ type: "reset" });
return (
<div>
<h1>Welcome to my counter</h1>
<p>Count: {state.count}</p>
<button onClick={addFive}>Add 5</button>
<button onClick={reset}>Reset</button>
</div>
);
}
import AppTSX from "./App.tsx";
export default App = AppTSX;
Ми використовуємо TypeScript у кількох ключових місцях:
interface State
описує форму стану редуктора.type CounterAction
описує різні дії, які можна надсилати до редуктора.const initialState: State
надає тип для початкового стану, а також тип, який використовуєтьсяuseReducer
за замовчуванням.stateReducer(state: State, action: CounterAction): State
встановлює типи для аргументів та значення, що повертається функцією-редуктором.
Більш явною альтернативою встановленню типу на initialState
є надання аргументу типу до useReducer
:
import { stateReducer, State } from './your-reducer-implementation';
const initialState = { count: 0 };
export default function App() {
const [state, dispatch] = useReducer<State>(stateReducer, initialState);
}
useContext
Хук useContext
Хук - це техніка передачі даних по дереву компонентів без необхідності передавати пропси через компоненти. Він використовується при створенні компонента-постачальника і часто при створенні хука для споживання значення у дочірньому компоненті.
Тип значення, наданого контекстом, виводиться зі значення, переданого у виклик createContext
:
import { createContext, useContext, useState } from 'react';
type Theme = "light" | "dark" | "system";
const ThemeContext = createContext<Theme>("system");
const useGetTheme = () => useContext(ThemeContext);
export default function MyApp() {
const [theme, setTheme] = useState<Theme>('light');
return (
<ThemeContext.Provider value={theme}>
<MyComponent />
</ThemeContext.Provider>
)
}
function MyComponent() {
const theme = useGetTheme();
return (
<div>
<p>Current theme: {theme}</p>
</div>
)
}
import AppTSX from "./App.tsx";
export default App = AppTSX;
Ця техніка працює, коли у вас є значення за замовчуванням, яке має сенс - але іноді бувають випадки, коли його немає, і в таких випадках null
може бути розумним значенням за замовчуванням. Однак, щоб система типів розуміла ваш код, вам потрібно явно вказати ContextShape | null
на createContext
.
Це призводить до того, що вам потрібно усунути | null
у типі для контекстних споживачів. Наша рекомендація полягає у тому, щоб хук перевіряв його існування під час виконання і видавав помилку, якщо він відсутній:
import { createContext, useContext, useState, useMemo } from 'react';
// This is a simpler example, but you can imagine a more complex object here
type ComplexObject = {
kind: string
};
// The context is created with `| null` in the type, to accurately reflect the default value.
const Context = createContext<ComplexObject | null>(null);
// The `| null` will be removed via the check in the Hook.
const useGetComplexObject = () => {
const object = useContext(Context);
if (!object) { throw new Error("useGetComplexObject must be used within a Provider") }
return object;
}
export default function MyApp() {
const object = useMemo(() => ({ kind: "complex" }), []);
return (
<Context.Provider value={object}>
<MyComponent />
</Context.Provider>
)
}
function MyComponent() {
const object = useGetComplexObject();
return (
<div>
<p>Current object: {object.kind}</p>
</div>
)
}
useMemo
Хуки useMemo
створюватимуть/знову отримуватимуть доступ до запам'ятованого значення з виклику функції, перезапускаючи функцію лише при зміні залежностей, переданих як 2-й параметр. Результат виклику хука виводиться зі значення, що повертається з функції у першому параметрі. Ви можете бути чіткішими, надавши хуку аргумент типу.
// The type of visibleTodos is inferred from the return value of filterTodos
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
useCallback
useCallback
забезпечує стабільне посилання на функцію доти, доки залежності, передані у другий параметр, є однаковими. Як і useMemo
, тип функції виводиться зі значення, що повертається функцією у першому параметрі, і ви можете бути більш чіткими, надавши хуку аргумент типу.
const handleClick = useCallback(() => {
// ...
}, [todos]);
Під час роботи у суворому режимі TypeScript useCallback
потрібно додати типи для параметрів у вашому зворотному виклику. Це пов'язано з тим, що тип зворотного виклику виводиться зі значення, що повертається функцією, а без параметрів тип не може бути повністю зрозумілим.
Залежно від ваших уподобань щодо стилю коду, ви можете використовувати функції *EventHandler
з React-типів, щоб надати тип для обробника події одночасно з визначенням зворотного виклику:
import { useState, useCallback } from 'react';
export default function Form() {
const [value, setValue] = useState("Change me");
const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
setValue(event.currentTarget.value);
}, [setValue])
return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}
Корисні типи
Існує досить великий набір типів, які походять з пакету @types/react
, його варто прочитати, коли ви відчуєте себе комфортно у взаємодії React та TypeScript. Ви можете знайти їх в папці React в DefinitelyTyped. Тут ми розглянемо деякі з найбільш поширених типів.
Події DOM
При роботі з DOM-подіями в React тип події часто можна визначити з обробника події. Однак, коли ви хочете витягти функцію для передачі в обробник події, вам потрібно буде явно задати тип події.
import { useState } from 'react';
export default function Form() {
const [value, setValue] = useState("Change me");
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setValue(event.currentTarget.value);
}
return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}
import AppTSX from "./App.tsx";
export default App = AppTSX;
Існує багато типів подій, передбачених у React-типах - повний список можна знайти тут, який базується на найпопулярніших подіях з DOM.
Для визначення типу, який ви шукаєте, ви можете спочатку переглянути інформацію про наведення для обробника події, який ви використовуєте, де буде показано тип події.
Якщо вам потрібно використати подію, якої немає у цьому списку, ви можете використати тип React.SyntheticEvent
, який є базовим для всіх подій.
Діти
Існує два поширених способи опису дочірніх компонентів. Перший полягає у використанні типу React.ReactNode
, який є об'єднанням усіх можливих типів, що можуть бути передані як дочірні в JSX:
interface ModalRendererProps {
title: string;
children: React.ReactNode;
}
Це дуже широке визначення дочірніх елементів. Другий - використовувати тип React.ReactElement
, який є лише елементами JSX, а не примітивами JavaScript, такими як рядки або числа:
interface ModalRendererProps {
title: string;
children: React.ReactElement;
}
Зауважте, що ви не можете використовувати TypeScript для опису того, що дочірні елементи є певним типом елементів JSX, тому ви не можете використовувати систему типів для опису компонента, який приймає лише <li>
дочірні елементи.
Приклад обох React.ReactNode
та React.ReactElement
з перевіркою типу можна побачити у цій пісочниці TypeScript.
Стилі пропсів
При використанні вбудованих стилів у React ви можете використовувати React.CSSProperties
для опису об'єкта, переданого в проп style
. Цей тип є об'єднанням усіх можливих CSS-властивостей і є гарним способом переконатися, що ви передаєте правильні CSS-властивості до пропу style
, а також отримати автозаповнення у вашому редакторі.
interface MyComponentProps {
style: React.CSSProperties;
}
Подальше навчання
Цей гайд розглянув основи використання TypeScript з React, але є ще багато чого, чого варто навчитися. Окремі сторінки API у документації можуть містити більш детальну документацію щодо їх використання з TypeScript.
Ми рекомендуємо наступні ресурси:
Посібник з TypeScript є офіційною документацією для TypeScript і охоплює більшість ключових особливостей мови.
У примітках до випуску TypeScript детально описано кожну нову можливість.
React TypeScript Cheatsheet - шпаргалка для використання TypeScript з React, що підтримується спільнотою, охоплює багато корисних крайніх випадків та надає більше інформації, ніж цей документ.
TypeScript Community Discord - чудове місце, де можна поставити запитання та отримати допомогу з проблемами TypeScript та React.