网站搜索

reactjs - 带有其 props 的 React 组件数组的类型定义


我正在制作一个 React 组件,它接受一个包含其他 React 组件及其 props 的数组。

type Components = [ComponentType, Props][]

我该如何编写这个,以便 TypeScript 能理解 Props 对象应该与给定的组件属性匹配?

谢谢。

更新,基本上一个函数接受一个由 [fn, args] 元组组成的数组,并使用对应的 args 调用每个 fn。如何编写该函数的签名,以使这些元组在类型上是安全的、正确的 fn/arg 配对?

答案#1

你可以做到这一点,尽管这有点复杂,而且在当前的实现中,它比我希望的要脆弱一些。我已经将类型逻辑抽象到一个辅助类型中:

// This part is reusable for any pair of [unaryFunction, argument]
type IsFnArgPair<T extends any> = T extends [infer F, infer A] ? F extends (x: A) => any ? T : [never, never] : [never, never]

function RenderChildPropPairs<T extends any[]>(props: { pairs: Array<IsFnArgPair<T>> }): JSX.Element {
  return (
    <>
      {props.pairs.map(([Comp, props]) => <Comp {...props} />)}
    </>
  );
}

我们可以定义几个组件/属性对来测试这个:

type MyComponentProps = { children: React.ReactNode, foo: number }
function MyComponent(props:MyComponentProps): JSX.Element {
  return (
    <div>
      {String(props.foo)}
      {props.children}
    </div>
  );
}

type MyOtherComponentProps = {
  foo: boolean
}

function MyOtherComponent({ foo }: MyOtherComponentProps): JSX.Element {
  return <div>{String(foo)}</div>;
}

然后测试它:

function RenderPass() {
  // OK
  return <RenderChildPropPairs pairs={[[MyComponent, { foo: 1, children: null }], [MyOtherComponent, { foo: true }]]} />
}

function RenderFail() {
  // Fails as expected
  return <RenderChildPropPairs pairs={[[MyComponent, { foo:'hi', children: null }], [MyOtherComponent, { foo: 1 }]]} />
}

这并不完美。特别是有两个方面你可以潜在地改进:更好的智能提示(如果有人传递了无效的配对,他们只会看到“类型 blah 不能分配给类型 'never'”,这不是一个很好的错误信息)。第二是,如果你尝试将配对提取到一个变量中,你会看到推断方面的问题,尽管可以通过使用 satisfies 来略微缓解这个问题:

function LessOops() {
  const pairs = [[MyComponent, { foo: 1, children: null }], [MyOtherComponent, { foo: true }]] satisfies Array<[any, any]>;
  return <RenderChildPropPairs pairs={pairs} />
}

请注意,尽管使用了any,这仍然是类型安全的:如果你传入一个无效的配对,你会得到一个错误。

这个解决方案的另一个问题是,如果你尝试使用 as const 将配对数组输入为元组,你会看到一个与 as const 的 readonly 特性相关的错误。我会继续尝试整理这个问题,尽管也许有更聪明的人可以提供更好的解决方案。

操场

答案#2

尝试使用 TypeScript 泛型为你的组件数组及其对应的 props 定义类型。方法如下:

import { ComponentType } from 'react';

// Define a generic type for props
type Props<T extends ComponentType<any>> = T extends ComponentType<infer P> ? P : never;

// Define a type for an array of component-prop pairs
type Components<T extends [ComponentType<any>, any][]> = {
  [K in keyof T]: T[K] extends [ComponentType<any>, any] ? [T[K][0], Props<T[K][0]>] : never;
};

// Example usage
const components: Components<[
  [React.FC<{ name: string }>, { name: string }],
  [React.FC<{ age: number }>, { age: number }]
]> = [
  [Component1, { name: 'John' }],
  [Component2, { age: 25 }]
];

Props 类型使用条件类型来推断给定组件类型的 props 类型。Components 类型接受组件-属性对数组,并确保每个组件的 props 与指定的 props 类型匹配。

然后你可以使用 Components 类型来定义你的组件数组以及它们的属性。