简体   繁体   English

Typescript React 组件的通用类型

[英]Typescript generic type for a React component

I'm trying to figure out how to write the correct type for the Data entry so that it accepts any component with props我试图弄清楚如何为数据条目编写正确的类型,以便它接受任何带有道具的组件

Example:例子:

const A = (props: { a: number }) => <>{props.a}</>
const B = (props: { b: string }) => <>{props.b}</>

type Data = {
  [id: string]: <P>(props: P) => JSX.Element | null
}

const data: Data = {
  aa: (props: Parameters<typeof A>[0]) => A(props),
  bb: (props: Parameters<typeof B>[0]) => B(props)
}

const Usage = () => (
  <>
    A: {data.aa({ a: 12 })}
    B: {data.bb({ b: 'hi' })}
  </>
)

Even though I've specified prop types for each component, I'm still getting this error:即使我已经为每个组件指定了道具类型,我仍然会收到此错误:

TS2322: Type '(props: Parameters<typeof A>[0]) => JSX.Element' is not assignable to type '<P>(props: P) => Element | null'.
  Types of parameters 'props' and 'props' are incompatible.
    Type 'P' is not assignable to type '{ a: number; }'

What should I write in the Data type so it accepts any component?我应该在Data类型中写什么以便它接受任何组件?

Provided you have installed @types/react and @types/react-dom you will have most react types already defined.如果您已经安装了@types/react@types/react-dom ,您将拥有已经定义的大多数反应类型。

For Function Components there is the type FunctionComponent or FC for short.对于 Function 组件,有类型FunctionComponent或简称FC It is also generic so you can specify the props type.它也是通用的,因此您可以指定道具类型。

import type { FC } from 'react';

const A: FC<{ a: number }> = ({ a }) => <>{a}</>;
const B: FC<{ b: number }> = ({ b }) => <>{b}</>;

type Data = {
  [id: string]: FC<any>;
};

const data: Data = {
  aa: A,
  bb: B,
};

const Usage = () => (
  <>
    A: {data.aa({ a: 12 })}
    B: {data.bb({ b: 'hi' })}
  </>
);

demo: https://stackblitz.com/edit/react-ts-y9ejme?file=App.tsx演示: https://stackblitz.com/edit/react-ts-y9ejme?file=App.tsx


Note however you lose the props type by typing an object as Data , because you're allowing any props types.但是请注意,您通过键入 object 作为Data丢失了道具类型,因为您允许任何道具类型。 Typescript will not see that b is supposed to be a number, not a string. Typescript 不会看到b应该是一个数字,而不是一个字符串。 It would be better to exclude that type and just let typescript infer the type from the object literal.最好排除该类型,让 typescript 从 object 文字推断类型。

const A: FC<{ a: number }> = ({ a }) => <>{a}</>;
const B: FC<{ b: number }> = ({ b }) => <>{b}</>;

const data = {
  aa: A,
  bb: B,
};

export default function App() {
  return (
    <>
      A: {data.aa({ a: 12 })}
      {/* Error: Type 'string' is not assignable to type 'number'. */}
      B: {data.bb({ b: 'hi' })} 
    </>
  );
}

demo: https://stackblitz.com/edit/react-ts-j8uagi?file=App.tsx演示: https://stackblitz.com/edit/react-ts-j8uagi?file=App.tsx


Otherwise, you'll have to explicitly type the properties:否则,您必须显式键入属性:

const A: FC<{ a: number }> = ({ a }) => <>{a}</>;
const B: FC<{ b: number }> = ({ b }) => <>{b}</>;

type Data = {
  aa: typeof A;
  bb: typeof B;
};

const data: Data = {
  aa: A,
  bb: B,
};

export default function App() {
  return (
    <>
      A: {data.aa({ a: 12 })}
      {/* Error: Type 'string' is not assignable to type 'number'. */}
      B: {data.bb({ b: 'hi' })}
    </>
  );
}

demo: https://stackblitz.com/edit/react-ts-ykbq8q?file=App.tsx演示: https://stackblitz.com/edit/react-ts-ykbq8q?file=App.tsx


Or ensure that the properties you are accessing are common for all components:或者确保您访问的属性对所有组件都是通用的:

const A: FC<{ a: number }> = ({ a }) => <>I'm an A {a}</>;
const B: FC<{ a: number, b: number }> = ({ a, b }) => <>I'm a B {a}</>;

type Data = {
  [id: string]: FC<{ a: number }>;
};

const data: Data = {
  aa: A,
  bb: B,
};

export default function App() {
  return (
    <>
      A: {data.aa({ a: 12 })}
      {/* Error: Type 'string' is not assignable to type 'number'. */}
      B: {data.bb({ a: 'hi' })}
      {/* You can pass b here, but it won't be typechecked */}
    </>
  );
}

demo: https://stackblitz.com/edit/react-ts-atcwwh?file=App.tsx演示: https://stackblitz.com/edit/react-ts-atcwwh?file=App.tsx


There's other options, but it really depends on your use case.还有其他选择,但这实际上取决于您的用例。

I like what Chris Hamilton mentioned with using functional components class from react.我喜欢 Chris Hamilton 提到的使用来自 React 的功能组件 class。 I read this question more about how to deal with typescript generic types with constraints.我更多地阅读了这个问题,了解如何处理带有约束的 typescript 泛型类型。 The best I could see is to create a union type of AProps and BProps for defining the type constraint in the Data type definition and using typeguards to enforce what is used in each Data function. It however, would get unwieldy if you want this to work for any type of component at all because you have to define the union type for all possible components.我能看到的最好的是创建 AProps 和 BProps 的联合类型,用于在数据类型定义中定义类型约束,并使用类型保护来强制执行每个数据 function 中使用的内容。但是,如果您希望它起作用,它会变得笨拙对于任何类型的组件,因为您必须为所有可能的组件定义联合类型。

 type AProps = { a: number }; type BProps = { b: string }; const A = (props: AProps) => <>{props.a}</> const B = (props: BProps) => <>{props.b}</> type GenericParams = AProps | BProps; type Data = { [id: string]: <P extends GenericParams>(props: P) => JSX.Element | null } const data: Data = { aa: (props: GenericParams) => isAProps(props)? A(props): null, bb: (props: GenericParams) => isBProps(props)? B(props): null } const Usage = () => ( <> A: {data.aa({ a: 12 })} B: {data.bb({ b: 'hi' })} </> ) function isAProps(props: GenericParams): props is AProps { if ("a" in props && typeof props.a === "number") return true; return false; } function isBProps(props: GenericParams): props is BProps { if ("b" in props && typeof props.b === "string") return true; return false; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM