در این مقاله قصد داریم تا با روش‌هایی که می‌توانیم با استفاده از آن‌ها تایپ درست مربوط به Event را تعیین کنیم، آشنا شویم.

هنگامی که با React و تایپ اسکریپت کار می‌کنیم، اغلب با این نوع خطا مواجه می‌شویم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const onChange = (e) => {};
Parameter 'e' implicitly has an 'any' type.
<input onChange={onChange} />;
const onChange = (e) => {}; Parameter 'e' implicitly has an 'any' type. <input onChange={onChange} />;
const onChange = (e) => {};
Parameter 'e' implicitly has an 'any' type.
 
<input onChange={onChange} />;

همیشه مشخص نیست که باید چه تایپی را به

e
e در داخل تابع
onChange
onChangeبدهیم.

این مشکل می‌تواند با

onClick
onClick،
onSubmit
onSubmitیا هر event handler دیگری که المنت‌های DOM را دریافت می کند اتفاق بیفتد.

برای حل این مشکل چندین راه حل وجود دارد که در ادامه مقاله آن‌ها را بررسی خواهیم کرد.

راه حل ۱: از ماوس برای تشخیص تایپ Handler استفاده کنیم

اولین راه حل این است که ماوس را روی تایپ چیزی که می‌خواهیم به آن منتقل کنیم، نگه داریم. به عنوان مثال اگر ماوس را روی

onChange
onChangeنگه داریم خواهیم دید:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<input onChange={onChange} />;
(property) React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
<input onChange={onChange} />; (property) React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
<input onChange={onChange} />;
          
(property) React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined

همانطور که می‌بینیم یک تایپ طولانی را در خروجی نمایش می‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
React.InputHTMLAttributes<HTMLInputElement>.onChange?:
React.ChangeEventHandler<HTMLInputElement> | undefined
React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
React.InputHTMLAttributes<HTMLInputElement>.onChange?:
  React.ChangeEventHandler<HTMLInputElement> | undefined

بخشی که ما می‌خواهیم این است:

React.ChangeEventHandler<HTMLInputElement>
React.ChangeEventHandler<HTMLInputElement>. می‌توانیم از آن برای نوشتن تابع
onChange
onChangeخود استفاده نماییم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
const onChange: React.ChangeEventHandler<
HTMLInputElement
> = (e) => {
console.log(e);
};
<input onChange={onChange} />;
import React from "react"; const onChange: React.ChangeEventHandler< HTMLInputElement > = (e) => { console.log(e); }; <input onChange={onChange} />;
import React from "react";
 
const onChange: React.ChangeEventHandler<
  HTMLInputElement
> = (e) => {
  console.log(e);
};
 
<input onChange={onChange} />;

راه حل ۲: یک تابع Inline بنویسیم سپس تایپ Event را مشخص کنیم

گاهی اوقات ما نمی‌خواهیم کل تابع را بنویسیم، فقط قصد داریم event را داشته باشیم.

برای اینکه بتوانیم تایپ درست مناسب event را extract کنیم باید رفتار متفاوتی داشته باشیم. ابتدا یک تابع inline در داخل

onChange
onChangeبسازیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<input onChange={(e) => {}} />
<input onChange={(e) => {}} />
<input onChange={(e) => {}} />

اکنون به

e
eدسترسی داریم، می‌توانیم ماوس را روی آن قرار دهیم و تایپ صحیح event را دریافت کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<input onChange={(e) => {}} />
(parameter) e: React.ChangeEvent<HTMLInputElement>
<input onChange={(e) => {}} /> (parameter) e: React.ChangeEvent<HTMLInputElement>
<input onChange={(e) => {}} />
                 
(parameter) e: React.ChangeEvent<HTMLInputElement>

در نهایت، می‌توانیم آن تایپ را کپی کرده و از آن برای نوشتن تابع

onChange
onChangeخود استفاده نماییم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
const onChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
console.log(e);
};
<input onChange={onChange} />;
import React from "react"; const onChange = ( e: React.ChangeEvent<HTMLInputElement> ) => { console.log(e); }; <input onChange={onChange} />;
import React from "react";
 
const onChange = (
  e: React.ChangeEvent<HTMLInputElement>
) => {
  console.log(e);
};
 
<input onChange={onChange} />;

راه حل ۳: از React.ComponentProps استفاده کنیم

یک راه برای سرعت بخشیدن به این امر حذف مرحله‌ای است که در آن تایپ handler را بررسی می‌کنیم. خیلی خوب است که ما بتوانیم تایپ handler مورد نظرمان را تعیین کنیم و تایپ اسکریپت بقیه موارد را مشخص کند.

برای این کار می‌توانیم از یک تایپ کمکی به نام

ComponentProps
ComponentPropsاستفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
const onChange: React.ComponentProps<"input">["onChange"] =
(e) => {
console.log(e);
};
<input onChange={onChange} />;
import React from "react"; const onChange: React.ComponentProps<"input">["onChange"] = (e) => { console.log(e); }; <input onChange={onChange} />;
import React from "react";
 
const onChange: React.ComponentProps<"input">["onChange"] =
  (e) => {
    console.log(e);
  };
 
<input onChange={onChange} />;

با ارسال

input
inputبه
ComponentProps
ComponentProps، به تایپ اسکریپت می‌گوییم که ما props را برای المنت
input
inputمی‌خواهیم.

سپس، ویژگی

onChange
onChangeرا از آن props می‌گیریم و از آن برای نوشتن تابع خود استفاده می‌کنیم.

راه حل ۴: از EventFrom Helper استفاده کنیم

این مورد بسیار خوب است، اما هنوز مجبور هستیم تابع

onChange
onChangeرا بنویسیم. اما اگر بخواهیم فقط تایپ event را extract کنیم چه کاری باید انجام دهیم؟

برای این کار می‌توانیم از ترکیبی از

Parameters
Parameters،
NonNullable
NonNullableو access typeهای ایندکس شده برای رسیدن به آنجا استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
const onChange = (
e: Parameters<
NonNullable<React.ComponentProps<"input">["onChange"]>
>[0]
) => {};
import React from "react"; const onChange = ( e: Parameters< NonNullable<React.ComponentProps<"input">["onChange"]> >[0] ) => {};
import React from "react";
 
const onChange = (
  e: Parameters<
    NonNullable<React.ComponentProps<"input">["onChange"]>
  >[0]
) => {};

اما این کد بسیار زیاد است. در عوض، بیایید یک تایپ کمکی به نام

EventFor
EventForرا تصور کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const onChange = (e: EventFor<"input", "onChange">) => {
console.log(e);
};
<input onChange={onChange} />;
const onChange = (e: EventFor<"input", "onChange">) => { console.log(e); }; <input onChange={onChange} />;
const onChange = (e: EventFor<"input", "onChange">) => {
  console.log(e);
};
 
<input onChange={onChange} />;

این تایپ کمکی، تایپ المنت و تایپ handler را می‌گیرد و تایپ event را return می‌کند. به این ترتیب، روی هر یک از پارامترها autocomplete دریافت می‌کنیم و نیازی نیست تابع را بنویسیم.

اما مشکلی که وجود دارد این است که ما باید یک تایپ کمکی نسبتا بزرگ در پایگاه کد خود نگه داریم. این کد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type GetEventHandlers<
T extends keyof JSX.IntrinsicElements
> = Extract<keyof JSX.IntrinsicElements[T], `on${string}`>;
/**
* Provides the event type for a given element and handler.
*
* @example
*
* type MyEvent = EventFor<"input", "onChange">;
*/
export type EventFor<
TElement extends keyof JSX.IntrinsicElements,
THandler extends GetEventHandlers<TElement>
> = JSX.IntrinsicElements[TElement][THandler] extends
| ((e: infer TEvent) => any)
| undefined
? TEvent
: never;
type GetEventHandlers< T extends keyof JSX.IntrinsicElements > = Extract<keyof JSX.IntrinsicElements[T], `on${string}`>; /** * Provides the event type for a given element and handler. * * @example * * type MyEvent = EventFor<"input", "onChange">; */ export type EventFor< TElement extends keyof JSX.IntrinsicElements, THandler extends GetEventHandlers<TElement> > = JSX.IntrinsicElements[TElement][THandler] extends | ((e: infer TEvent) => any) | undefined ? TEvent : never;
type GetEventHandlers<
  T extends keyof JSX.IntrinsicElements
> = Extract<keyof JSX.IntrinsicElements[T], `on${string}`>;
 
/**
 * Provides the event type for a given element and handler.
 *
 * @example
 *
 * type MyEvent = EventFor<"input", "onChange">;
 */
export type EventFor<
  TElement extends keyof JSX.IntrinsicElements,
  THandler extends GetEventHandlers<TElement>
> = JSX.IntrinsicElements[TElement][THandler] extends
  | ((e: infer TEvent) => any)
  | undefined
  ? TEvent
  : never;

استفاده از کدام راه حل بهتر است؟

به طور کلی استفاده از راه حل

EventFor
EventForتوصیه می‌شود. به این دلیل که:

اما اگر دلایل بالا برای ما قابل قول نبود، راه حل

ComponentProps
ComponentPropsیک جایگزین عالی برای این کار است.