reactjs - تایپ یک تگ داینامیک در React با استفاده از TypeScript؟


چطور میتوانم یک تگ پویا در React با TypeScript تایپ کنم؟ با توجه به این کد:

interface CompProps {
  tag: string;
}

const MyComponent: React.FunctionComponent<CompProps> = ({
  tag = "div",
  children
}) => {
  const Wrapper = tag;

  return <Wrapper>{children}</Wrapper>;
};

من این خطا را دریافت میکنم:

نوع '{ children: ReactNode; }' هیچ ویژگی مشترکی با نوع 'IntrinsicAttributes' ندارد. ts(2559)

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

پاسخ شماره ۱

شما میتوانید یک string را به عنوان نام تگ وارد کنید و همانطور که استفاده کردهاید از آن استفاده کنید، اما باید آن را به درستی تایپ کنید تا بررسی نوع کار کند. tag باید یک کلید از JSX.IntrinsicElements باشد.

interface CompProps {
  tag: keyof JSX.IntrinsicElements;
}

const MyComponent: React.FunctionComponent<CompProps & React.HTMLAttributes<HTMLOrSVGElement>> = ({
  tag: Wrapper = "div",
  children,
  ...rest
}) => {
  return <Wrapper {...rest}>{children}</Wrapper>;
};

لینک زمین بازی

پاسخ شماره ۲

استفاده از یک تعریف نوع برای تمام عناصر HTML

برای اینکه اجازه داده شود همه عناصر HTML به عنوان تگ شما استفاده شوند، میتوانید از کلیدهای رابط IntrinsicElements که در فضای نام JSX تعریف شده است، استفاده کنید. IntrinsicElements به نظر میرسد شامل نگاشتی از تگهای عناصر HTML به ویژگیهای مربوط به آنها باشد (شامل ویژگیهای خاص عنصر). برای استفاده از این کلیدها میتوانیم کارهای زیر را انجام دهیم:

interface Props {
  tag?: keyof JSX.IntrinsicElements
}

اگر بخواهم اجازه دهم که از کامپوننتهای React به عنوان تگ استفاده شود، چه؟

React دو رابط را تعریف میکند: ComponentClass و FunctionComponent. React همچنین یک اجتماع از این دو رابط را تعریف میکند که به شما امکان میدهد هر کامپوننت React را مشخص کنید: ComponentType. ما میتوانیم یک اجتماع از این و تعریف قبلی خود ایجاد کنیم تا هر دو کامپوننت و تگهای HTML را مجاز کنیم.

import { ComponentType } from 'react';

interface Props {
  tag?: ComponentType | keyof JSX.IntrinsicElements;
}

خب، حالا من یک تگ دارم، پس مشخصههای HTML چی؟

اگر میخواهید اجازه دهید تمام ویژگیهای دیگر HTML نیز مجاز باشند، میتوانید یا React.HTMLAttributes<Element> را گسترش دهید تا همه ویژگیهای مشترک HTML (بدون ویژگیهای خاص هر عنصر) را به دست آورید، یا میتوانید یک نوع عمومی معرفی کنید و از JSX.IntrinsicElements استفاده کنید.

گزینه دوم پیچیدهتر است و با چند نکته قابل توجه همراه است. شما باید از type به جای interface برای گسترش/تقاطع Props خود و ویژگیهای خاص تعریفشده در یک کلید در JSX.IntrinsicElements استفاده کنید. شما همچنین نیاز خواهید داشت که از جنریکها در تابع خود استفاده کنید تا بتوانید آنها را به نوع Props خود پاس دهید، که به این معنی است که دیگر نمیتوانید از React.FunctionComponent<Props> استفاده کنید زیرا این قبل از دسترسی به هر جنریک انجام میشود. این به این معنی است که میخواهید children را به تعریف Props خود اضافه کنید.

این مقدار زیادی کلمه بود که معتقدم با این مثال بهتر توضیح داده میشود:

// Define our Props type to allow the specifying of a Tag for HTML attributes
// Also define children as React does with React.ReactNode
type Props<Tag extends keyof JSX.IntrinsicElements> = {
  tag?: ComponentType | keyof JSX.IntrinsicElements;
  children?: ReactNode;
} & JSX.IntrinsicElements[Tag];

// Define our generic (Tag) again here and give it our default value
// Don't forget to specify the type Props<Tag> at the end of your function's arguments
// Then we can spread all props to the tag/Wrapper
function MyComponent<Tag extends keyof JSX.IntrinsicElements = 'div'>({ tag: Wrapper = 'div', ...props }: Props<Tag>) {
  return <Wrapper {...props} />;
}

// Example usage, noValidate is typed as
// (JSX attribute) React.FormHTMLAttributes<HTMLFormElement>.noValidate?: boolean | undefined
<MyComponent<'form'> tag="form" noValidate>
  {/* My Form Stuff */}
</MyComponent>;

// You don't need to specify 'div' since it is the default
<MyComponent id="page">
  <p>Just a paragraph inside of a regular div</p>
</MyComponent>;