استخدام Typescript

استخدام TypeScript هو طريقة شائعة لإضافة تعريفات النوع لأكواد JavaScript التقليدية. بطريقة مبتكرة، TypeScript تدعم JSX ويمكنك الحصول على الدعم Typescript لمكتبة React بشكل كامل عند إضافة @types/react و @types/react-dom لمشروعك.

التحميل

جميع أُطر عمل React الإنتاجية توفر دعماً لاستخدام TypeScript. اتبع إرشادات إطار العمل التي تبيّن طريقة التحميل:

إضافة TypeScript لمشروع React موجود بالفعل

لتحميل آخر نسخة من تعريفات الأنواع الخاصة بمكتبة React، استخدم الأمر:

Terminal
npm install @types/react @types/react-dom

خيارات المترجم الآتية عليك إعدادها في ملف ‘tsconfig.json’ الخاص بك:

  1. يجب أن تُضمّن dom في lib (ملاحظة: إذا لم يكن خيار lib محدداً،dom هو مضمّن بشكل افتراضي).
  2. jsx عليك إعدادها بواحد من الخيارات الصالحة.يجب أن تكون preserve كافية لمعظم التطبيقات. إذا كنت تريد نشر مكتبة، اتبع إرشادات توثيقات jsx لتعرف ما القيمة التي يجب اختيارها.

Typescript مع مكونات react

Note

كل ملف يحتوي على JSX يجب أن يمتلك اسمه اللاحقة .tsx . هذه اللاحقة هي إضافة مخصصة ل TypeScript والتي تخبر TypeScript بأنّ هذا الملف يحتوي على JSX.

كتابة TypeScript مع React هي مشابهة جداً لطريقة كتابة JavaScript مع React. الاختلاف الوحيد هو عند العمل مع مكون ما، حيث يمكنك أن توفر أنواعاً لخصائص مكونك (props). هذه الأنواع يمكن أن تستخدم للتحقق الصحيح وتوفير توثيقات خطية في المحرر.

لنأخذ المكون MyButton من إرشادات البداية السريعة, يمكننا إضافة وصف لنوع title الخاص بالزر:

function MyButton({ title }: { title: string }) {
  return (
    <button>{title}</button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>مرحباً بك في تطبيقي</h1>
      <MyButton title="أنا زر" />
    </div>
  );
}

Note

هذه ال sandboxes تسطيع التعامل مع كود TypeScript, لكنها لا تقوم بتشغيل متحقق-للنوع. هذا يعني أنه يمكنك تعديل أكواد TypeScript في sandboxes للتعلم, لكن لن تسطيع الحصول على أي أخطاء أو تحذيرات بشأن النوع. لكي تحصل على التحقق من النوع type-checking, يمكنك استخدام TypeScript Playground أو استخدم sandbox آخر مليئاً بالميزات متصلاً بالإنترنت.

هذه الصيغة الخطية هي أبسط طريقة لتأمين الأنواع لمكون، ولكن بمجرد امتلاكه عدة حقول وعليك وصفها تصبح تلك الطريقة غير عملية. لذلك يمكنك أن تستخدم الواجهات 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 للمكونات في هذا الرابط Object Types ولكن إذا كنت أيضاً مهتماً باستخدام Union Types لوصف خاصية يمكن أن يكون نوعها واحداً من عدة أنواع مختلفة قليلاً و إرشادات إنشاء أنواع من أنواع أخرى لحالات استخدام أكثر تقدّماً.

أمثلة الخطافات

تعريفات النوع من @types/react تتضمن أنواعاً للخطافات المدمجة مع React، لذلك يمكنك استخدامهم في مكونك بدون أي إعداد إضافي. يتم بناؤها لتأخد الأكواد التي تكتبها في مكونك بعين الاعتبار، لذلك تسطيع الحصول على أنواع مستنتجة في معظم الأوقات وبشكل مثالي لا تحتاج للتعامل مع تفاصيل تأمين الأنواع.

علي أي حال، يمكننا إلقاء نظرة على القليل من الأمثلة في كيفية تزويد الخطافات بالأنواع.

useState

الخطاف useState سيعيد استخدام القيمة التي تم تمريرها إليه كقيمة ابتدائية ليحدد ما هو نوع القيمة.مثال:

// استنتاج النوع كنوع "boolean"
const [enabled, setEnabled] = useState(false);

سيقوم بإسناد النوع boolean للقيمة enabled، و setEnabled ستكون دالة تقبل إما boolean كمعامل، أو دالة تقوم بإرجاع boolean. إذا كنت تريد تقدم نوعاً بشكل صريح للحالة، يمكنك فعل ذلك بتقديم نوع لمعاملل لكي تستدعي useState:

// استنتاج النوع كنوع "boolean"
const [enabled, setEnabled] = useState<boolean>(false);

إنها ليست مفيدة جداً في هذه الحالة، لكن الحالة الأكثر شيوعاً حيث ربما تريد تقديم نوع يكون عندما تمتلك union type. مثال، status هنا يمكن أن تكون واحدة من نصوص مختلفة قليلاً:

type Status = "idle" | "loading" | "success" | "error";

const [status, setStatus] = useState<Status>("idle");

أو، كما أوصي في الاسس لهيكلة الحالات, يمكنك تجميع الحالات المرتبطة ككائن وتقوم بوصف الاحتمالات الممكنة من خلال أنواع الكائن:


تعريفات النوع من `@types/react` تتضمن أنواعاً للخطافات المدمجة مع React، لذلك يمكنك استخدامهم في مكونك بدون أي إعداد إضافي. يتم بناؤها لتأخد الأكواد التي تكتبها في مكونك بعين الاعتبار، لذلك تسطيع الحصول على [أنواع مستنتجة](https://www.typescriptlang.org/docs/handbook/type-inference.html) في معظم الأوقات وبشكل مثالي لا تحتاج للتعامل مع تفاصيل تأمين الأنواع.

علي أي حال، يمكننا إلقاء نظرة على القليل من الأمثلة في كيفية تزويد الخطافات بالأنواع.

### `useState` {/*typing-usestate*/}

[الخطاف `useState`](/reference/react/useState) سيعيد استخدام القيمة التي تم تمريرها إليه كقيمة ابتدائية ليحدد ما هو نوع القيمة.مثال:

```ts
// استنتاج النوع كنوع "boolean"
const [enabled, setEnabled] = useState(false);

سيقوم بإسناد النوع boolean للقيمة enabled، و setEnabled ستكون دالة تقبل إما boolean كمعامل، أو دالة تقوم بإرجاع boolean. إذا كنت تريد تقدم نوعاً بشكل صريح للحالة، يمكنك فعل ذلك بتقديم نوع للمعامل لكي تستدعي useState:

// استنتاج النوع كنوع "boolean"
const [enabled, setEnabled] = useState<boolean>(false);

إنها ليست مفيدة جداً في هذه الحالة، لكن الحالة الأكثر شيوعاً حيث ربما تريد تقديم نوع يكون عندما تمتلك union type. مثال، 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 هو خطاف أكثر تعقيداً يأخذ دالة reducer و حالة ابتدائية. الأنواع لدالة reducer تستنتج من الحالة الإبتدائية. بشكل اختياري يمكنك تقديم نوع argement لإستدعاء الخطاف 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("فعل غير معروف");
  }
}

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 تصف الشكل لحالة ال 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 هو تقنية لتمرير البيانات لشجرة المكونات بدون الحاجة لتمرير الخصائص عبر المكونات. إنها تستخدم عن طريق إنشاء مكون مزود وغالباً عن طريق إنشاء خطاف ليستخدم القيمة في مكون ابن.

نوع القيمة المزودة من قبل 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> السمة الحالية: {theme} </p>
    </div>
  )
}

هذه التقنية تعمل عندما يكون لديك قيمة افتراضية منطقية لكن في بعض الأحيان يوجد حالات لا تكون كذلك، وفي تلك الحالات null معقولة كقيمة افتراضية. على أي حال، لتسمح لنظام النوع أن يفهم كودك، أن تحتاج أن تضبط بشكل صريح ContextShape | null على createContext:

هذا يسبب المشكلة التي تحتاج لإزلة | null في نوع مستهلك ال context. توصياتنا بأن تجعل الخطاف يقوم بفحص وقت التشغيل ليتحقق من وجودها ويرمي خطأً عندما تكون موجودة:

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 يؤمن مرجعاً مستقراً لدالة طالما بقيت الاعتمادات التي تم تمريرها للمعامل الثاني نفسها. تماماً مثل `، نوع الدالة مستنتج من القيمة الراجعة من الدالة في المعامل الأول، ويمكنك أن تكون أكثر صراحة بتقديم نوع المعامل للخطاف.

const handleClick = useCallback(() => {
// ...
}, [todos]);

عندما تعمل بالوضع الصارم ل TypeScript useCallbck تحتاج لإضافة الأنواع للمعاملات في دالة ال callback الخاصة بك. هذا بسبب أن نوع ال callback مسنتنج من القيمة الراجعة من الدالة، وبدون المعاملات النوع لن يفهم بشكل كامل.

حسب تفضيلات أسلوب كودك، يمكنك استخدام EventHandler*، وهي دوال من أنواع React لتأمين النوع للدوال التي تتعامل مع الأحداث بنفس الوقت الذي يتم تعريف دالة ال callback: Depending on your code-style

import { useState, useCallback } from 'react';

export default function Form() {
const [value, setValue] = useState("غيّرني");

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("غيّرني");

  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 playground.

خاصيات Style

عند استخدام الأنماط المضمنة في React، يمكنك استخدام “reud.cssproperties” لوصف الكائن الذي تم تمريره إلى خاصية style. هذا النوع هو اتحاد لجميع خصائص CSS الممكنة، وهي طريقة جيدة للتأكد من أنك تمر خصائص CSS صالحة إلى خاصية style، والحصول على أكمال تلقائي في المحرر الخاص بك.

interface MyComponentProps {
style: React.CSSProperties;
}

تعلم أبعد

قد غطى هذا الدليل أساسيات استخدام TypeScript مع React، ولكن هناك الكثير لكي تعلمه. قد تحتوي صفحات API الفردية في المستندات على وثائق أكثر متعمقة حول كيفية استخدامها مع TypeScript.

نوصي بالموارد التالية:

  • The TypeScript handbook التوثيق الرسمي للغة TypeScript، يغطي معظم الميزات المفتاحية للغة.

  • The TypeScript release notes يتضمن كل الميزات الجديدة بشكل متعمق.

  • React TypeScript Cheatsheet هو ورقة تحتوي رؤوس أقلام يتم صيانتها من قبل المجتمع لاستخدام TypeScript مع React، تغطي الكثير من حالات الحافة المفيدة وتوفير المزيد من التوسع أكثر من هذا المستند.