استفاده از TypeScript
تایپ اسکریپت (TypeScript) روشی محبوب برای افزودن تعاریف انواع داده به کدهای JavaScript است. به صورت پیشفرض، TypeScript از JSX پشتیبانی میکند و میتوانید با افزودن بسته @types/react و @types/react-dom به پروژه، پشتیبانی کاملی از React داشته باشید.
نصب
تمام فریمورکهای React سطح محیط عملیاتی از TypeScript پشتیبانی میکنند. برای نصب، به راهنمای مختص هر فریمورک مراجعه کنید:
افزودن TypeScript به پروژه React موجود
برای نصب جدیدترین نسخه تعاریف نوع React دستور زیر را اجرا کنید:
npm install @types/react @types/react-dom
گزینههای کامپایلر زیر باید در فایل tsconfig.json شما تنظیم شده باشند:
- گزینه
domباید درlibگنجانده شود (نکته: اگر هیچ مقداری برایlibمشخص نشود،domبه طور پیشفرض در نظر گرفته میشود). - مقدار
jsxباید با یکی از گزینههای معتبر تنظیم شود. مقدارpreserveبرای اکثر برنامهها کفایت می کند. اگر قصد دارید کتابخانه ای منتشر میکنید، بهمستندات jsxدر مورد انتخاب مقدار مربوط به این گزینه مراجعه کنید.
TypeScript با کامپوننتهای React
هر فایلی که شامل JSX است باید از پسوند .tsx استفاده کند. این یک پسوند خاص TypeScript است و به آن اعلام می کند که فایل حاوی سینتکس JSX است.
نوشتن TypeScript با React بسیار شبیه به نوشتن JavaScript با React است. تفاوت کلیدی هنگام کار با یک کامپوننت زمان تعریف props کامپوننت است که باید نوع ورودی را هم مشخص کنید. این انواع میتوانند برای بررسی صحت در زمان توسعه و بیلد و همچنین ارائه مستندات درونخطی در ویرایشگرها استفاده شوند.
کامپوننت MyButton از راهنمای شروع سریع را دوباره بررسی کنید، میتوانیم برای توصیف title نوع string را به ورودی دکمه اضافه کنیم:
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 یا type برای توصیف props کامپوننت خود مانند کد زیر استفاده کنید:
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>
);
}
نوع توصیف کننده props کامپوننت شما میتواند به سادگی یا به پیچیدگی که نیاز دارید باشد، هرچند باید از نوع شی ای باشد که با یا type یا interface مشخص شده است. شما میتوانید درباره چگونگی تعیین نوع اشیا در TypeScript مطلب انواع اشیا را مطالعه کنید، همچنین با استفاده از اتحاد انواع ویژگی های را متشکل از چند نوع مختلف تعریف کنید و همچنین برای یادگیری استفاده پیشرفته تر می توانید راهنمای ایجاد انواع از انواع موجود را مطالعه کنید.
نمونههایی از هوک ها (Hooks)
تعاریف نوع درون بسته @types/react شامل تعاریف انواع هوکهای داخلی React نیز می شود، بنابراین میتوانید از آنها در کامپوننتهای خود بدون هیچ تنظیمات اضافی ای استفاده کنید. استفاده از این بسته تعاریف به گونهای عمل می کند که کد داخل کامپوننت را نیز در بر می گیرد، به همین دلیل همیشه انواع استخراج شده از کدتان را بدون نیاز به تعریف نوع متغیر، در ویرایشگر در اختیار شما قرار میگیرد.
با این حال، میتوانیم به چند مثال در مورد چگونگی ارائه انواع در زمان استفاده از Hooks نگاهی بیندازیم.
هوک useState
هوک useState مقدار پاس شده را بعنوان حالت اولیه استفاده میکند تا نوع مقدار وضعیت را تعیین کند. به عنوان مثال:
// مقدار بولین با استفاده از این کد به صورت خودکار استخراج می شود
const [enabled, setEnabled] = useState(false);
این کد به متغیر enabled نوع boolean اختصاص میدهد و تابع setEnabled آرگومانی از نوع boolean یا تابعی که یک boolean برمیگرداند را بعنوان ورودی دریافت می کند. اگر میخواهید نوعی را به طور صریح برای وضعیت مشخص کنید، میتوانید این کار را با ارائه یک نوع جنریک در زمان فراخوانی useState انجام دهید:
// مستقیما نوع بعنوان بولین مشخص شده است
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>
);
}
ما در این کد از 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 روشی برای پاس دادن دادهها در سلسله مراتب کامپوننت ها بدون نیاز به props است. این روش با ایجاد یک کامپوننت ارائهدهنده و اغلب با ایجاد یک هوک برای استفاده از مقدار در کامپوننت فرزند انجام میشود.
نوع مقداری که توسط context ارائه میشود از مقداری که به فراخوانی 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>
)
}
روش مورد استفاده زمانی جواب می دهد که یک مقدار پیشفرض با نوع مشخص دارید - اما گاهی اوقات مواردی وجود دارد مقدار اولیه مشخص نیست و در این موارد null میتواند به عنوان یک مقدار پیشفرض استفاده شود. با این حال، برای اینکه سیستم نوع بتواند کد شما را درک کند، نیاز به اعلام صریح ContextShape | null در زمان فراخوانی تابع createContext دارید.
این کار باعث میشود که در مصرف کنندگان باید حالت | null را مدیریت کنید. توصیه می شود در هوک مصرف مقدار نال بودن آن بررسی شده و در صورت عدم وجود مقدار، خطای تولید شود:
import { createContext, useContext, useState, useMemo } from 'react';
// یک نوع کامپلکس برای مقدار کانتکست
type ComplexObject = {
kind: string
};
// مقدار اولیه برابر نال در نظر گرفته شده است
const Context = createContext<ComplexObject | 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>Current object: {object.kind}</p>
</div>
)
}
هوک useMemo
هوک useMemo مقدار بازگشتی از فراخوانی تابع را ذخیره میکند و یعنی هر بار دسترسی به متغیر، باعث بازگشت مقدار اولین فراخوانی می شود. مگر اینکه وابستگی های مشخص شده در پارامتر دوم تابع تغییر کنند که باعث اجرای مجدد تابع با متغیر های جدید می شود. نوع نتیجه فراخوانی هوک از مقدار برگشتی تابع استنباط میشود. میتوانید همچنین با ارائه یک آرگومان می توانید به صراحت نوع بازگشتی را مشخص کنید.
// 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 نیاز به افزودن انواع برای پارامترها در callback دارد. این به این دلیل است که نوع callback از مقدار برگشتی تابع استنباط میشود و بدون پارامترها نمیتوان نوع را به طور کامل درک کرد.
بسته به ترجیحات سبک کد نویسی شما، میتوانید از توابع *EventHandler از انواع React برای مشخص کردن نوع هندلر رویداد، همزمان با تعریف callback استفاده کنید:
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>
</>
);
}
تعداد زیادی نوع رویداد در انواع 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 ویژگیهای جدید را به طور عمیق پوشش میدهد.
- راهنمای تقلب TypeScript برای React یک راهنمای جامعهمدیریتی برای استفاده از TypeScript با React است که بسیاری از موارد مفید در موارد لبه و بیشتری از این سند را پوشش میدهد.
- Discord جامعه TypeScript مکانی عالی برای پرسش سوالات و دریافت کمک در موارد TypeScript و React است.