Выкарыстанне TypeScript
TypeScript — папулярны спосаб дадаць тыпізацыю ў ваш JavaScript код. TypeScript падтрымлівае JSX з каробкі, вы можаце дадаць паўнавартасную падтрымку React для вэб, дадаўшы да праекта @types/react
і @types/react-dom
.
You will learn
Усталёўка
Усе React фрэймворкі, гатовыя для выкарыстання ў працоўным асяроддзі прапануюць падтрымку TypeScript. Для кожнага фрэймворка трымайцеся ягоных уласных інструкцый:
Дадаванне TypeScript у існуючы праект React
Для ўсталявання апошняй версіі тыпаў React выкарыстоўвайце:
Ваш tsconfig.json
мае змяшчаць наступныя налады:
dom
мае быць уключанае ўlib
(Заўвага: калі значэнне дляlib
не зададзенае,dom
прадвызначана уключанае).jsx
мае мець любое валіднае значэнне.preserve
мае падыходзіць для большасці выпадкаў. Калі вы публікуеце бібліятэку, звярніцеся да дакументацыі па полюjsx
каб падабраць правільнае значэнне.
TypeScript з кампанентамі React
Напісанне TypeScript з React вельмі падобнае да напісання JavaScript з React. Асноўнае адрозненне ў тым, што падчас працы з кампанентамі вы зможаце ўказаць тыпы пропсаў. Гэтыя тыпы могуць быць скарыстаныя для праверкі правільнасці і прадастаўлення ўнутранай дакументацыі для рэдактараў кода.
Узяўшы кампанент MyButton
са старонкі «Хуткі старт», мы можам дадаць апісанне тыпу для пропса title
кнопкі:
function MyButton({ title }: { title: string }) { return ( <button>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Вітаю ў маёй праграме</h1> <MyButton title="Я — кнопка" /> </div> ); }
Дадзены аднарадковы сінтаксіс — найпрасцейшы спосаб тыпізацыі кампанента, але з павелічэннем колькасці пропсаў, дадзеная канструкцыя пачне станавіцца грузнай. Замест гэтага можна выкарыстоўваць interface
ці type
для апісання пропсаў кампанента:
interface MyButtonProps { /** Тэкст, які будзе паказвацца ў кнопцы */ title: string; /** Задае, ці можна ўзаемадзейнічаць з кнопкай */ disabled: boolean; } function MyButton({ title, disabled }: MyButtonProps) { return ( <button disabled={disabled}>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Вітаю ў маёй праграме</h1> <MyButton title="Я — адключаная кнопка" disabled={true}/> </div> ); }
Тып, якім вы апісваеце пропсы кампанента, можа быць на столькі простым ці складаным, на колькі вам тое патрэбна, але гэта заўсёды мае быць апісанне тыпу аб’екта праз type
ці interface
. Вы можаце даведацца больш аб тым, як апісваць аб’екты ў TypeScript на старонцы «Тыпы аб’ектаў». Таксама вас могуць зацікавіць аб’яднаныя тыпы для апісання пропсаў, якія могуць адпавядаць аднаму з некалькіх тыпаў, ці інструкцыі як ствараць тыпы на аснове іншых тыпаў для больш складаных выпадкаў.
Прыклады хукаў
Апісанні тыпаў у @types/react
уключае таксама і тыпізацыю ўбудаваных хукаў, таму вы можаце выкарыстоўваць іх у сваіх кампанентах без дадатковай налады. Яны пабудаваныя такім чынам, каб улічваць код, ужо напісаны ў кампаненце, такім чынам вы атрымаеце аўтаматычна вызначаныя тыпы і ў большасці выпадкаў вам не давядзецца разбірацца з дробнай тыпізацыяй самастойна.
Але далей мы разгледзім некалькі прыкладаў, як тыпізаваць хукі.
useState
Хук useState
будзе выкарыстоўваць значэнне, зададзенае пры ініцыалізацыі, для аўтаматычнага вызначэння тыпу. Напрыклад:
// Тут будзе вызначаны тып boolean
const [enabled, setEnabled] = useState(false);
Для enabled
будзе вызначаны тып boolean
, а setEnabled
будзе прымаць або boolean
, або функцыю, якая вяртае boolean
. Калі вы хочаце відавочна перадаць тып для вашага стану, вы можаце зрабіць гэта, паказаўшы тып аргумента пры выкліку useState
:
// Тып відавочна зададзены як boolean
const [enabled, setEnabled] = useState<boolean>(false);
Гэта не надта карысна ў дадзеным выпадку, але вам можа спатрэбіцца падобнае, калі вы маеце аб’яднаны тып. Напрыклад, тут статус можа прымаць толькі пэўныя радковыя значэнні:
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>Вітаю на старонцы свайго лічыльніка</h1> <p>Колькасць: {state.count}</p> <button onClick={addFive}>Дадаць 5</button> <button onClick={reset}>Скінуць</button> </div> ); }
Мы выкарыстоўваем 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>Цяперашняя тэма: {theme}</p> </div> ) }
Дадзеная тэхніка працуе калі маецца прадвызначанае значэнне, якое мае сэнс, але бываюць сітуацыі, калі такога няма. У такім выпадку разумна будзе скарыстацца null
у якасці прадвызначанага значэння. Але каб сістэме тыпізацыі было зразумела, трэба відавочна ўказаць тып ContextShape | null
для createContext
.
Разам з тым з’яўляецца патрэба выключаць | null
пры атрыманні значэння. Мы раім дадаць у хук праверку, якая будзе падчас выканання правяраць наяўнасць значэння і выкідваць памылку пры яго адсутнасці:
import { createContext, useContext, useState, useMemo } from 'react';
// Гэта просты прыклад, але вы можаце прыдумаць больш складаны аб’ект
type ComplexObject = {
kind: string
};
// Кантэкст створаны з ужываннем `| null`, каб дакладна адлюстроўваць прадвызначанае значэнне
const Context = createContext<ComplexObject | null>(null);
// `| null` будзе выключаны пасля таго, як спрацуе хук.
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>Бягучы аб’ект: {object.kind}</p>
</div>
)
}
useMemo
Хук useMemo
дапамагае ствараць і паўторна атрымліваць доступ да запомненых вынікаў запуску функцыі. Функцыя будзе выконвацца наноў толькі ў выпадках, калі зменіцца залежнае значэнне, перададзенае другім параметрам. Вынік выкліку функцыі будзе аўтаматычна тыпізаваны адпаведна функцыі, што была перададзеная першым параметрам. Вы можаце тыпізаваць хук відавочна, перадаўшы яму тып.
// Тып visibleTodos вызначаны аўтаматычна з тыпу значэння, што вяртае функцыя 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}</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}</p> </> ); }
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 параметры і з’яўляецца добрым спосабам пераканацца, што вы перадаяце правільны пропс style
, і атрымаць аўтадапаўненне ў рэдактары кода.
interface MyComponentProps {
style: React.CSSProperties;
}
Далейшае вывучэнне
Дадзеная старонка тлумачыць базавае выкарыстанне TypeScript з React, але ёсць яшчэ шмат, што вартае вывучэння. Асобныя старонкі дакументацыі API могуць змяшчаць больш дэтальную дакументацыю, як выкарыстоўваць іх з TypeScript.
Мы раім наступныя рэсурсы:
-
The TypeScript handbook — афіцыйная дакументацыя TypeScript, якая тлумачыць большасць асноўных функцый мовы.
-
The TypeScript release notes падрабязна тлумачыць кожную новую функцыю.
-
React TypeScript Cheatsheet — мадэруемая супольнасцю шпаргалка па выкарыстанні TypeScript з React, што тлумачыць шмат карысных выпадкаў і забяспечвае ахоп большы за гэтую старонку.
-
TypeScript супольнасць у Discord — выдатнае месца, каб задаць пытанні і атрымаць дапамогу па пытаннях TypeScript і React.