繁体   English   中英

Typescript React 组件的通用类型

[英]Typescript generic type for a React component

我试图弄清楚如何为数据条目编写正确的类型,以便它接受任何带有道具的组件

例子:

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' })}
  </>
)

即使我已经为每个组件指定了道具类型,我仍然会收到此错误:

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; }'

我应该在Data类型中写什么以便它接受任何组件?

如果您已经安装了@types/react@types/react-dom ,您将拥有已经定义的大多数反应类型。

对于 Function 组件,有类型FunctionComponent或简称FC 它也是通用的,因此您可以指定道具类型。

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' })}
  </>
);

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


但是请注意,您通过键入 object 作为Data丢失了道具类型,因为您允许任何道具类型。 Typescript 不会看到b应该是一个数字,而不是一个字符串。 最好排除该类型,让 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' })} 
    </>
  );
}

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


否则,您必须显式键入属性:

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' })}
    </>
  );
}

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


或者确保您访问的属性对所有组件都是通用的:

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 */}
    </>
  );
}

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


还有其他选择,但这实际上取决于您的用例。

我喜欢 Chris Hamilton 提到的使用来自 React 的功能组件 class。 我更多地阅读了这个问题,了解如何处理带有约束的 typescript 泛型类型。 我能看到的最好的是创建 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