هنگامی که بر روی یک پروژه React کار می‌کنیم، احتمالاً تعریف یک تایپ برای React props رایج‌ترین فعالیت تایپ اسکریپتی است که ما انجام خواهیم داد. بحث‌هایی در مورد بهترین روش برای تعریف props وجود دارد. در این مقاله قصد داریم تا مزایا و معایب هر روش را باهم بررسی کنیم.

سه روش تعریف props

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

Inline Object Literals

props را می‌توانیم با استفاده از یک inline object تعریف کنیم.

import { ReactNode } from "react";
 
const Wrapper = (props: {
  children?: ReactNode;
}) => {
  return <div>{props.children}</div>;
};

تایپ‌های prop را می‌توانیم destruct کرده و با سینتکس {}: {}بنویسیم.

import { ReactNode } from "react";
 
const Wrapper = ({
  children,
}: {
  children?: ReactNode;
}) => {
  return <div>{children}</div>;
};

Type Aliaseها

تایپ‌های Prop همچنین می‌توانند به یک type alias نیز extract شوند:

import { ReactNode } from "react";
 
export type WrapperProps = {
  children?: ReactNode;
};
 
const Wrapper = (props: WrapperProps) => {
  return <div>{props.children}</div>;
};

Type aliaseها همیشه باید همراه با کامپوننت export شوند، به این ترتیب در صورت نیاز می‌توانیم از آن‌ها در فایل‌های دیگر استفاده کنیم.

const Wrapper = ({ children }: WrapperProps) => {
  return <div>{children}</div>;
};

Interfaceها

Interfaceها راه دیگری برای تعریف props هستند:

import { ReactNode } from "react";
 
export interface WrapperProps {
  children?: ReactNode;
}
 
const Wrapper = (props: WrapperProps) => {
  return <div>{props.children}</div>;
};

درست مانند type aliaseها، Interfaceها باید همیشه export شوند تا بعداً بتوانیم دوباره از آن‌ها استفاده کنیم. همینطور می‌توانیم Interfaceها همانند type aliaseها destruct کنیم.

const Wrapper = ({ children }: WrapperProps) => {
  return <div>{children}</div>;
};

اکنون با همه روش‌ها آشنا شدیم، اما قبل از تصمیم‌گیری نهایی بهتر است همه جوانب مثبت و منفی آن‌ها را هم باهم بررسی کنیم.

استفاده از یک code snippet  برای React Props

مزیت اصلی تعریف props با استفاده از inline object سرعت بسیار بالای آن است. می‌توانیم خیلی سریع props را تایپ کرده و به کدنویسی ادامه دهیم.

اما مشکلی که وجود دارد این است که inline objectها در نهایت باید به یک تایپ extract شوند. بنابراین اگر در حال نوشتن یک کامپوننت هستیم، احتمالاً باید از type aliase یا Interface استفاده کنیم. برای برطرف کردن این مشکل، توصیه می‌شود code snippet خود را برای type aliaseها و Interfaceها بنویسیم.

در ادامه یک code snippet داریم که برای ساختن کامپوننت‌های خود در VSCode از آن استفاده می‌کنیم.

{
  "component": {
    "prefix": "comp",
    "body": [
      "export interface $1Props {",
      "  $۲",
      "}",
      "",
      "export const $1 = (props: $1Props) => {",
      "  return $3",
      "}"
    ]
  }
}

تکمیل خودکار به compبه یک کامپوننت با یک Interface توسعه پیدا می‌کند که هر دو export شده‌اند.

Interfaces over Intersections(Interfaceها بر Intersectionها)

به طور خلاصه، توصیه می‌شود که استفاده از type aliasها ارجحیت بیشتری داشته باشد، زیرا ویژگی declaration merging در interfaceمی‌تواند برای مبتدی‌ها کمی گیج‌کننده باشد.

اما یک مورد خاص وجود دارد که در بهتر است در این شرایط به جای type aliaseها از interfaceها استفاده کنیم. این مورد خاص ایجاد intersectionهای پیچیده از props است.

فرض کنید می‌خواهیم یک کامپوننت ایجاد کنیم که تمام props مربوط به inputرا دارد اما نیاز به اضافه کردن یک labelprops هست. ما باید از type helper  به نام ComponentPropsextend پیدا کنیم.

استفاده از typealias می‌تواند وسوسه‌انگیز باشد:

import { ComponentProps } from "react";
 
export type InputProps =
  ComponentProps<"input"> & {
    label: string;
  };
 
export function Input({
  label,
  ...props
}: InputProps) {
  return (
    <label>
      {label}
      <input {...props} />
    </label>
  );
}

اما متأسفانه intersectionهایی که از این طریق استفاده می‌شوند، در مقیاس یک پایگاه کد بزرگ سرعت تایپ اسکریپت را کاهش می‌دهند.

در عوض، باید از یک interface استفاده کنیم، با استفاده از interface extends:

import { ComponentProps } from "react";
 
export interface InputProps
  extends ComponentProps<"input"> {
  label: string;
}

این توصیه از ویکی‌پدیا عملکرد تایپ اسکریپت و همچنین یک PR از پایگاه کد Sentry است که با حذف این intersectionها به نفع interfaceها، بررسی تایپ آن‌ها را تسریع می‌بخشد. بنابراین در شرایطی که تایپ‌های دیگر را extend می‌کنیم، بهتر است از interfaceها استفاده کنیم.

جمع‌بندی

در این مقاله سعی کردیم تا روش‌های مختلف تعریف props با تایپ اسکریپت را باهم بررسی کنیم. به طور کلی، ما برای تعریف props باید از interfaceها استفاده کنیم. آن‌ها بیشترین عملکرد را دارند و با استفاده از یک code snippet، سریع‌تر نوشته می‌شوند.

اگر قبلاً برای تعریف props از inline objectها یا typeاستفاده می‌کردیم جای نگرانی وجود ندارد. زیرا نیازی به refactor کردن پروژه نیست و برنامه به درستی کار می‌کند.

اما اگر احساس کردیم که در پایگاه کدی که داریم شروع به کند شدن سرعت در IDE کرده است، باید intersectionها را در props خود بررسی کنیم. اگر موردی را پیدا کردیم، سعی کنیم آن‌ها را با interface extendsrefactor کنیم.