【问题标题】:How to handle props injected by HOC in React with Typescript?如何在 React with Typescript 中处理 HOC 注入的 props?
【发布时间】:2018-06-28 13:16:10
【问题描述】:

我创建了一个简单的 HOC,它在组件中注入了一个方法 translate

export interface IMessageProps {
  translate: (key: string) => string;
}

export const message = <P extends object>(
  Component: React.ComponentType<P & IMessageProps>
): React.SFC<P & IMessageProps> => (props: P) => {

  const translate = (key: string): string => messages[key];

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

用法:

class MyComponent extends React.Component<IMessageProps, {}> {
  render() {
    return (
      <>{this.props.translate('hello.world')}</>
    );
  }
}

export default message(MyComponent);

当我想调用我的组件 &lt;MyComponent/&gt; 时出现问题,因为 tsc 抱怨属性 translate 没有传递给 MyComponent 并期望像 &lt;MyComponent translate={...}/&gt; 这样的东西。

Type '{}' is not assignable to type 'IntrinsicAttributes & IMessageProps & { children?: ReactNode; }'.
  Type '{}' is not assignable to type 'IMessageProps'.
    Property 'translate' is missing in type '{}'.

所以我的问题是:如何绕过这个虚假错误?我不想在 IMessageProps 中设置 translate 可选,因为 tslint 会抱怨 Cannot invoke an object which is possibly 'undefined'

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    编辑

    Typescript 3.2 破坏了下面的代码。在 3.2 之前,除了 jsx 标记外,不允许使用泛型类型参数进行传播操作,并且在那里没有进行非常严格的检查。这个issue 改变了这一点。扩展操作没有得到更严格的检查,并且这打破了代码。我们可以做的最简单的调整是在props 上使用类型断言:

    export const message = <P extends IMessageProps>(
        Component: React.ComponentType<P>
    ): React.SFC<Pick<P, Exclude<keyof P, keyof IMessageProps>>> => (props: Pick<P, Exclude<keyof P, keyof IMessageProps>>) => {
    
        const translate = (key: string): string => messages[key];
    
        return <Component {...props as P} translate={translate} />;
    };
    

    3.2 之前

    您可以只从返回的SCF 中排除IMessageProps 的属性,使用PickPExclude 中选择属性以排除IMessageProps 的键

    export interface IMessageProps {
        translate: (key: string) => string;
    }
    
    export const message = <P extends IMessageProps>(
        Component: React.ComponentType<P>
    ): React.SFC<Pick<P, Exclude<keyof P, keyof IMessageProps>>> => (props: Pick<P, Exclude<keyof P, keyof IMessageProps>>) => {
    
        const translate = (key: string): string => messages[key];
    
        return <Component {...props} translate={translate} />;
    };
    
    
    class MyComponent extends React.Component<IMessageProps, {}> {
        render() {
            return (
                <>{this.props.translate('hello.world')}</>
            );
        }
    }
    
    const MyComponentWrapped = message(MyComponent);
    
    let d = <MyComponentWrapped /> // works
    

    3.5 及以上

    您可以使用Omit&lt;P, keyof IMessageProps&gt; 代替Pick&lt;P, Exclude&lt;keyof P, keyof IMessageProps&gt;&gt;

    【讨论】:

    • 像魅力一样工作。谢谢 !作为一种解决方法,我还通过强制转换导出使其工作:export default message(Install) as React.ComponentType&lt;{}&gt;.
    • @AlexandreAnnic 是的,如果您没有额外的属性,这也可以工作,但如果您的组件实际上有额外的属性,您需要再次指定它们。 Pick&lt;P, Exclude&lt;..&gt;&gt; 适用于任何 P :)
    • 是的,这显然是一种解决方法。不是真正的解决方案。最后我在定义我的包装器时犯了一个错误:我可以通过从React.SFC&lt;P &amp; IMessageProps&gt; 中删除IMessageProps 来解决我的问题。
    • 这似乎不适用于 typescript 3.2.4 :( Typescript 抱怨 Type '{ translate: (key: string) =&gt; string; }' is not assignable to type 'P' 我正在寻找解决方案,如果我找到了,会在这里分享。
    • @Logar 3.2 似乎破坏了代码,尤其是github.com/Microsoft/TypeScript/pull/28234
    猜你喜欢
    • 1970-01-01
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 2020-03-18
    • 1970-01-01
    • 2019-04-08
    • 1970-01-01
    相关资源
    最近更新 更多