如何使用 Typescript 在 React 中处理 HOC 注入的props?

IT技术 reactjs typescript
2021-04-28 10:13:37

我创建了一个简单的 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);

当我想调用我的组件时问题就出现了,<MyComponent/>因为 tsc 抱怨该属性translate没有传递给MyComponent并期望像<MyComponent translate={...}/>.

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

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

1个回答

编辑

Typescript 3.2 破坏了下面的代码。在 3.2 之前,除了 jsx 标签之外,不允许使用泛型类型参数进行传播操作,并且在那里没有非常严格地检查。这个问题改变了这一点。展开操作没有被更严格地检查,并且 this 会中断代码。我们可以做的最简单的调整是在 上使用类型断言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之前

您可以只IMessageProps从返回的SCF使用中排除 的属性,Pick从中选择属性PExclude排除 的键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<P, keyof IMessageProps>代替Pick<P, Exclude<keyof P, keyof IMessageProps>>