استخدام TypeScript
TypeScript هي طريقة شائعة لإضافة تعريفات الأنواع إلى قواعد أكواد JavaScript. بشكل افتراضي، يدعم TypeScript JSX ويمكنك الحصول على دعم كامل لـ React Web من خلال إضافة @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. الاختلاف الرئيسي عند العمل مع مكون هو أنه يمكنك توفير أنواع لـ props المكون. يمكن استخدام هذه الأنواع للتحقق من الصحة وتوفير التوثيق المضمن في المحررات.
مستعيرين مكون MyButton من دليل البداية السريعة، يمكننا إضافة نوع يصف title للزر:
هذا البناء المضمن هو أبسط طريقة لتوفير الأنواع لمكون، على الرغم من أنه بمجرد أن يكون لديك بعض الحقول لوصفها، يمكن أن يصبح غير عملي. بدلاً من ذلك، يمكنك استخدام interface أو type لوصف props المكون:
النوع الذي يصف props مكونك يمكن أن يكون بسيطًا أو معقدًا حسب حاجتك، على الرغم من أنه يجب أن يكون نوع كائن موصوفًا بـ type أو interface. يمكنك التعرف على كيفية وصف TypeScript للكائنات في أنواع الكائنات لكن قد تكون مهتمًا أيضًا باستخدام أنواع الاتحاد لوصف prop يمكن أن يكون واحدًا من عدة أنواع مختلفة ودليل إنشاء أنواع من أنواع لحالات الاستخدام الأكثر تقدمًا.
أمثلة Hooks
تعريفات الأنواع من @types/react تتضمن أنواعًا لـ Hooks المدمجة، لذا يمكنك استخدامها في مكوناتك دون أي إعداد إضافي. تم بناؤها لتأخذ في الاعتبار الكود الذي تكتبه في مكونك، لذا ستحصل على أنواع مستنتجة في كثير من الأحيان وبشكل مثالي لن تحتاج إلى التعامل مع تفاصيل توفير الأنواع.
ومع ذلك، يمكننا إلقاء نظرة على بعض الأمثلة حول كيفية توفير أنواع لـ Hooks.
useState
الـ useState Hook سيعيد استخدام القيمة الممررة كحالة أولية لتحديد ما يجب أن يكون نوع القيمة. على سبيل المثال:
// استنتاج النوع كـ "boolean" const [enabled, setEnabled] = useState(false);
سيعين هذا نوع boolean لـ enabled، و setEnabled ستكون دالة تقبل إما معامل boolean، أو دالة تُرجع boolean. إذا كنت ترغب في توفير نوع صريح للحالة، يمكنك القيام بذلك من خلال توفير معامل نوع لاستدعاء useState:
// تعيين النوع بشكل صريح إلى "boolean" const [enabled, setEnabled] = useState<boolean>(false);
هذا ليس مفيدًا جدًا في هذه الحالة، لكن حالة شائعة قد ترغب فيها في توفير نوع هي عندما يكون لديك نوع اتحاد. على سبيل المثال، status هنا يمكن أن يكون واحدًا من عدة strings مختلفة:
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 Hook هو Hook أكثر تعقيدًا يأخذ دالة reducer وحالة أولية. يتم استنتاج الأنواع لدالة reducer من الحالة الأولية. يمكنك اختياريًا توفير معامل نوع لاستدعاء useReducer لتوفير نوع للحالة، ولكن غالبًا ما يكون من الأفضل تعيين النوع على الحالة الأولية بدلاً من ذلك:
نحن نستخدم TypeScript في بعض الأماكن الرئيسية:
interface Stateيصف شكل حالة reducer.type CounterActionيصف الإجراءات المختلفة التي يمكن إرسالها إلى reducer.const initialState: Stateيوفر نوعًا للحالة الأولية، وأيضًا النوع الذي يستخدمهuseReducerبشكل افتراضي.stateReducer(state: State, action: CounterAction): Stateيعين الأنواع لمعاملات دالة reducer وقيمة الإرجاع.
بديل أكثر وضوحًا لتعيين النوع على 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 Hook هو تقنية لتمرير البيانات عبر شجرة المكونات دون الحاجة لتمرير props عبر المكونات. يتم استخدامه من خلال إنشاء مكون موفر وغالبًا من خلال إنشاء Hook لاستهلاك القيمة في مكون فرعي.
يتم استنتاج نوع القيمة المقدمة من السياق من القيمة الممررة إلى استدعاء createContext:
تعمل هذه التقنية عندما يكون لديك قيمة افتراضية منطقية - ولكن هناك حالات أحيانًا لا تكون لديك فيها، وفي هذه الحالات قد يبدو null معقولاً كقيمة افتراضية. ومع ذلك، للسماح لنظام الأنواع بفهم الكود الخاص بك، تحتاج إلى تعيين ContextShape | null بشكل صريح على createContext.
هذا يسبب مشكلة أنك تحتاج إلى إزالة | null في النوع لمستهلكي السياق. نوصي بأن يقوم Hook بفحص وجوده في وقت التشغيل ويطرح خطأ عندما لا يكون موجودًا:
import { createContext, useContext, useState, useMemo } from 'react'; // هذا مثال أبسط، لكن يمكنك تخيل كائن أكثر تعقيدًا هنا type ComplexObject = { kind: string }; // يتم إنشاء السياق مع `| null` في النوع، لتعكس بدقة القيمة الافتراضية. const Context = createContext<ComplexObject | null>(null); // سيتم إزالة `| null` عبر الفحص في 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 Hooks سينشئ/يعيد الوصول إلى قيمة محفوظة في الذاكرة من استدعاء دالة، ويعيد تشغيل الدالة فقط عندما تتغير التبعيات الممررة كمعامل ثانٍ. يتم استنتاج نتيجة استدعاء Hook من قيمة الإرجاع من الدالة في المعامل الأول. يمكنك أن تكون أكثر وضوحًا من خلال توفير معامل نوع لـ Hook.
// يتم استنتاج نوع visibleTodos من قيمة إرجاع filterTodos const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
useCallback
الـ useCallback يوفر مرجعًا مستقرًا لدالة طالما أن التبعيات الممررة إلى المعامل الثاني هي نفسها. مثل useMemo، يتم استنتاج نوع الدالة من قيمة إرجاع الدالة في المعامل الأول، ويمكنك أن تكون أكثر وضوحًا من خلال توفير معامل نوع لـ Hook.
const handleClick = useCallback(() => { // ... }, [todos]);
عند العمل في وضع TypeScript strict، يتطلب 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، يمكن غالبًا استنتاج نوع الحدث من معالج الحدث. ومع ذلك، عندما تريد استخراج دالة لتمريرها إلى معالج حدث، ستحتاج إلى تعيين نوع الحدث بشكل صريح.
There are many types of events provided in the React types - the full list can be found here which is based on the most popular events from the DOM.
When determining the type you are looking for you can first look at the hover information for the event handler you are using, which will show the type of the event.
If you need to use an event that is not included in this list, you can use the React.SyntheticEvent type, which is the base type for all events.
Children
هناك طريقتان شائعتان لوصف children المكون. الأولى هي استخدام نوع React.ReactNode، وهو اتحاد من جميع الأنواع الممكنة التي يمكن تمريرها كـ children في JSX:
interface ModalRendererProps { title: string; children: React.ReactNode; }
This is a very broad definition of children. The second is to use the React.ReactElement type, which is only JSX elements and not JavaScript primitives like strings or numbers:
interface ModalRendererProps { title: string; children: React.ReactElement; }
Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts <li> children.
You can see an example of both React.ReactNode and React.ReactElement with the type-checker in this TypeScript playground.
Style Props
When using inline styles in React, you can use React.CSSProperties to describe the object passed to the style prop. This type is a union of all the possible CSS properties, and is a good way to ensure you are passing valid CSS properties to the style prop, and to get auto-complete in your editor.
interface MyComponentProps { style: React.CSSProperties; }
Further learning
This guide has covered the basics of using TypeScript with React, but there is a lot more to learn. Individual API pages on the docs may contain more in-depth documentation on how to use them with TypeScript.
We recommend the following resources:
-
The TypeScript handbook is the official documentation for TypeScript, and covers most key language features.
-
The TypeScript release notes cover new features in depth.
-
React TypeScript Cheatsheet is a community-maintained cheatsheet for using TypeScript with React, covering a lot of useful edge cases and providing more breadth than this document.
-
TypeScript Community Discord is a great place to ask questions and get help with TypeScript and React issues.