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 类型来定义你的组件数组以及它们的属性。
