【问题标题】:passing a type argument for the props of a react component for a higher order component为高阶组件的反应组件的道具传递类型参数
【发布时间】:2020-11-09 23:43:12
【问题描述】:

我创建了一个playground of the problem

这里是代码:

type PropsWithChildren<P> = P & { children?: any };

interface FC<P = {}> {
    (props: PropsWithChildren<P>, context?: any): any;
}

type BaseFields = {
  label: string;
};

export type ComponentPropsType<C> = C extends FC<infer P> ? P : C;

export function renderFormControlDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<P>) {
  const Wrapped: FC<P> = ({ label, ...rest }) => {
    return <Comp label={label} {...rest} />;
  };


  return Wrapped;
}


export function renderFormControlNotDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<P>) {
  const Wrapped: FC<P> = (props) => {
    return <Comp {...props} label={props.label} />;
  };


  return Wrapped;
}

type SelectProps<OptionType> = {
    options: OptionType[];
} & BaseFields;

const Select: FC<SelectProps<unknown>> = ({options}) => {
    console.log(options)
}

const FormSelect1 = renderFormControlDestructured(Select);

const FormSelect2 = renderFormControlNotDestructured(Select);

console.log(FormSelect1, FormSelect2)

我希望 typescript 从传递的组件中推断出 Props,但我收到以下错误消息

总而言之,如果我为这样的高阶组件解构我的论点

export function renderFormControlDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<P>) {
  const Wrapped: FC<P> = ({ label, ...rest }) => {
    return <Comp label={label} {...rest} />;
  };


  return Wrapped;
}

我收到错误消息:

Type '{ label: string; } & Pick<PropsWithChildren<P>, "children" | Exclude<keyof P, "label">>' is not assignable to type 'PropsWithChildren<P>'.
Type '{ label: string; } & Pick<PropsWithChildren<P>, "children" | Exclude<keyof P, "label">>' is not assignable to type 'P'.
'{ label: string; } & Pick<PropsWithChildren<P>, "children" | Exclude<keyof P, "label">>' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'PropsWithChildren<BaseFields>'.

但如果我不重组,我不会得到错误

export function renderFormControlNotDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<P>) {
  const Wrapped: FC<P> = (props) => {
    return <Comp {...props} label={props.label} />;
  };


  return Wrapped;
}

我不明白为什么编译器允许一个实际上是相同的东西而不是另一个。

【问题讨论】:

  • 看起来编译器在协调解构类型时遇到了麻烦,即使它们在逻辑上是相同的。没有解构的替代解决方案将为您提供所需的结果。我建议坚持使用该解决方案。
  • 您能给我们一些反馈吗?

标签: reactjs typescript


【解决方案1】:

问题似乎在于解构PropsWithChildren&lt;P&gt;

export function renderFormControlDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<P>) {
  const Wrapped: FC<P> = ({ label, ...rest }) => {
    return <Comp label={label} {...rest} />;
  };

  return Wrapped;
}

这里restPick&lt;PropsWithChildren&lt;P&gt;, "children" | Exclude&lt;keyof P, "label"&gt;&gt; 类型,它与PropsWithChildren&lt;P&gt; 不同。

我们知道{ label: string; } &amp; Pick&lt;PropsWithChildren&lt;P&gt;, "children" | Exclude&lt;keyof P, "label"&gt;&gt; 类型等同于PropsWithChildren&lt;P&gt;,但它们显然是两种不同的类型。

如果我们在renderFormControlDestructured 声明中扩展FC 接口,我们有:

export function renderFormControlDestructured<P extends PropsWithChildren<BaseFields>>(Comp: (props: PropsWithChildren<P>, context?: any): any) {
  const Wrapped: (props: PropsWithChildren<P>, context?: any): any = ({ label, ...rest }) => {
    return <Comp label={label} {...rest} />;
  };

  return Wrapped;
}

所以propsPropsWithChildren&lt;P extends PropsWithChildren&lt;BaseFields&gt;&gt; 类型,实际上如果我们更改声明,从props 类型中删除一个嵌套PropsWithChildren

function renderFormControlDestructured<P extends PropsWithChildren<BaseFields>>(Comp: FC<BaseFields>)

错误消失。

嵌套多次PropsWithChildren实际上并没有改变类型,第一次添加可选的children属性,下一次用相同的属性替换该属性(实际上是noop);但似乎多个嵌套模板使 TypeScript 难以重建类型。

【讨论】:

    【解决方案2】:

    这是一个limitation of TypeScript,它与解构无关。此代码将产生相同的错误而无需任何解构:

    export function renderFormControlDestructured<
      P extends PropsWithChildren<BaseFields>
    >(Comp: ComponentType<P>) {
      // error in the next line
      const p: P = {
        label: "my label",
        children: "my children",
      };
    }
    

    通常,您将使用 HOC 向传递的组件添加一些内容。这是会产生相同错误的示例:

    import React, { PropsWithChildren, ComponentType } from "react";
    
    type BaseFields = {
      label: string;
      anotherProp: boolean;
    };
    
    export function renderFormControlDestructured<
      P extends PropsWithChildren<BaseFields>
    >(Comp: ComponentType<P>) {
      const Wrapped = (props: Omit<P, "anotherProp">) => {
        // error
        const newProps: P = {
          ...props,
          anotherProp: true,
        };
        return <Comp {...newProps} />;
      };
    
      return Wrapped;
    }
    

    一个简单的解决方法是使用类型断言:

    import React, { PropsWithChildren, ComponentType } from "react";
    
    type BaseFields = {
      label: string;
      anotherProp: boolean;
    };
    
    export function renderFormControlDestructured<
      P extends PropsWithChildren<BaseFields>
    >(Comp: ComponentType<P>) {
      const Wrapped = (props: Omit<P, "anotherProp">) => {
        // no error
        const newProps: P = {
          ...props,
          anotherProp: true,
        } as P;
        return <Comp {...newProps} />;
      };
    
      return Wrapped;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-30
      • 2021-11-23
      • 1970-01-01
      • 2019-06-26
      • 2017-01-31
      • 2019-03-26
      • 2020-06-19
      • 1970-01-01
      相关资源
      最近更新 更多