javascript 嵌套React组件属性的TypeScript类型推断

iaqfqrcu  于 2023-03-11  发布在  Java
关注(0)|答案(2)|浏览(201)

我正在努力用TypeScript正确地注解下面的React组件:

<MyComponent input={[Component, props]} />

input:应该是一个数组,其中第一个成员是React功能组件,第二个成员是包含其各自属性的对象。
对于本例,假设Component定义如下:

interface IComponentProps {
    data: number;
}

const Component: FC<IComponentProps> = ({data}) => {
    return <span>{data}</span>;
};

这是我目前对MyComponent所做的:

interface IDefault {
    [k: string]: unknown;
}

type TInput<T extends IDefault = IDefault> = [FC<T>, T];

interface IComponentProps {
    input: TInput;
}

const MyComponent: FC<IComponentProps> = ({input}) => {
    const [Component, props] = input;

    return <Component {...props} />;
};

不幸的是,当使用组件时,如下所示:

<MyComponent input={[Component, {data: 1}]} />

我得到:Property 'data' is missing in type 'IDefault' but required in type 'IOtherComponentProps'
这表明TypeScript没有推理 prop ,所以它使用的是默认值。
为了给予更多关于预期行为的信息,我期望,例如,在本例中:

<MyComponent input={[Component, {data: true}]} />

要获得错误,data应为number
我还尝试了以下类型替代(以及其他类型):

type TInput<T extends FC = FC<{[k: string]: unknown}>> = [
    T,
    ComponentPropsWithoutRef<T>
];

但没有成功。
有什么建议吗?

eimct9ow

eimct9ow1#

解决这个问题的一个方法是使用泛型类型并扩展React组件的props类型。

type ComponentProps<T extends React.ElementType> = T extends React.ComponentType<infer P> ? P : never;

type MyComponentProps<T extends React.ElementType> = {
  input: [T, ComponentProps<T>];
};

function MyComponent<T extends React.ElementType>({ input }: MyComponentProps<T>): JSX.Element {
  const [Component, props] = input;

  return <Component {...props} />;
}

在此实现中,我们定义了两个泛型类型:T是React组件类型,P是其props类型,我们使用ComponentProps类型从T中提取props类型。
然后,我们定义MyComponentProps类型,它将T作为泛型类型,并将输入prop定义为T及其prop的数组。
最后,在MyComponent函数中,我们对输入的prop进行解构,并使用其prop呈现Component。
通过这个实现,TypeScript将正确推断组件的属性类型,并在属性不是预期类型时引发错误。

interface IComponentProps {
  data: number;
}

const Component: React.FC<IComponentProps> = ({ data }) => {
  return <span>{data}</span>;
};

// Usage
<MyComponent input={[Component, { data: 1 }]} /> // OK
<MyComponent input={[Component, { data: true }]} /> // Type 'true' is not assignable to type 'number'
vu8f3i0k

vu8f3i0k2#

你是这个意思吗?

type RendererProps<M> = {
  input: [React.ComponentType<M>, M];
};

const MyComponentRenderer = <M,>({ input }: RendererProps<M>) => {
  const [Component, props] = input;
  return <Component {...props} />;
};

看起来很有效:

const TakesAFooProp = (props: { foo: string }) => {
  return <>{foo}</>;
};

// works
<MyComponentRenderer input={[TakesAFooProp, { foo: 'bar' }]} />;

// fails - foo is not assignable to number
<MyComponentRenderer input={[TakesAFooProp, { foo: 9 }]} />;

// fails - property foo is missing in type
<MyComponentRenderer input={[TakesAFooProp, { bar:'baz' }]} />;

相关问题