بررسی تایپ Event در React و تایپ اسکریپت

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

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

const onChange = (e) => {};
Parameter 'e' implicitly has an 'any' type.
 
<input onChange={onChange} />;

همیشه مشخص نیست که باید چه تایپی را به e در داخل تابع onChangeبدهیم.

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

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

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

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

<input onChange={onChange} />;
          
(property) React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined

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

React.InputHTMLAttributes<HTMLInputElement>.onChange?:
  React.ChangeEventHandler<HTMLInputElement> | undefined

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

import React from "react";
 
const onChange: React.ChangeEventHandler<
  HTMLInputElement
> = (e) => {
  console.log(e);
};
 
<input onChange={onChange} />;

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

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

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

<input onChange={(e) => {}} />

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

<input onChange={(e) => {}} />
                 
(parameter) e: React.ChangeEvent<HTMLInputElement>

در نهایت، می‌توانیم آن تایپ را کپی کرده و از آن برای نوشتن تابع onChangeخود استفاده نماییم:

import React from "react";
 
const onChange = (
  e: React.ChangeEvent<HTMLInputElement>
) => {
  console.log(e);
};
 
<input onChange={onChange} />;

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

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

برای این کار می‌توانیم از یک تایپ کمکی به نام ComponentPropsاستفاده نماییم.

import React from "react";
 
const onChange: React.ComponentProps<"input">["onChange"] =
  (e) => {
    console.log(e);
  };
 
<input onChange={onChange} />;

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

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

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

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

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

import React from "react";
 
const onChange = (
  e: Parameters<
    NonNullable<React.ComponentProps<"input">["onChange"]>
  >[0]
) => {};

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

const onChange = (e: EventFor<"input", "onChange">) => {
  console.log(e);
};
 
<input onChange={onChange} />;

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

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

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توصیه می‌شود. به این دلیل که:

  • این یک مکان واحد است که می‌توانیم برای دریافت تایپ event برای هر المنت و handlerای به آن مراجعه کنیم.
  • ما بر روی تایپ‌های المنت و handler یک autocomplete فعال داریم.

اما اگر دلایل بالا برای ما قابل قول نبود، راه حل ComponentPropsیک جایگزین عالی برای این کار است.

دیدگاه‌ها:

افزودن دیدگاه جدید