问题

我正在将React应用程序转换为使用大量函数编程模式的Typescript.其中最重要的模式是能够组合多个更高级组件以返回增强组件.我想要发生的是让Typescript在每个级别推断组件的类型并使用我传递的组件完成以上所有类型的HOC.

我在3.5.0和最新版本的Typescript上尝试了这段代码.在代码方面,我尝试使通用扩展我想要的类型<T extends FooProps>,将FC<T & BarProps>传递给返回函数组件,并扭转HOC的调用.

 export const withFoo = <T>(Component: FC<T & FooProps>): FC<Omit<T, 'foo'>> => props => (
  <Component {...props as T} foo="foo" />
)

export const withBar = <T>(Component: FC<T & BarProps>): FC<Omit<T, 'bar'>> => props => (
  <Component {...props as T} bar="bar" />
)

export const EnhancedComponent = withFoo(
  withBar(({ bar, foo }) => { // bar exists and foo does not exist on type 'PropsWithChildren<BarProps>'
    console.log(bar) // types come through (bar: string)
    console.log(foo) // types don't come through (foo: any) :(
    return <div>woohoo</div> 
  }),
)
 

我希望在HOC的每个级别都会遇到类型.在上面的例子中,我想在函数组件中看到这个我传递:

 export const EnhancedComponent = withFoo(
  withBar(({ bar, foo }) => { // both exist on the component
    console.log(bar) // bar: string
    console.log(foo) // foo: string
    return <div>woohoo</div>
  }),
)
 

  最佳答案

寻找高阶组件打字的好地方是其他包或@types包。

但是,您的用例有点有趣,因为您希望包装的组件接收更多的道具,每个包装高阶组件提供其中一个值。

鉴于这一点,这是一个潜在的解决方案.有几点需要指出:

  • 假设您想要允许任何组件类型(类或函数),达到 React.ComponentType 可能是您想要的.
  • 我尝试避免播放,但看起来这仍然是TypeScript的一个缺点(问题).这两个问题看起来相关:#28884 & #28748
  • 添加Omit作为参考,因为在3.5之前它不存在(我在Codesandbox中测试,这时仍在3.3)
 import * as React from "react";
import { render } from "react-dom";

// This exists in TypeScript 3.5+
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

interface FooProps {
  foo: string;
}

interface BarProps {
  bar: string;
}

export const withFoo = <Props extends FooProps>(
  Component: React.ComponentType<Props>
): React.ComponentType<Omit<Props, keyof FooProps>> => props => (
  <Component {...props as Props} foo="foo" />
);

export const withBar = <Props extends BarProps>(
  Component: React.ComponentType<Props>
): React.ComponentType<Omit<Props, keyof BarProps>> => props => (
  <Component {...props as Props} bar="bar" />
);

const WrappedComponent = ({ bar, foo, more }) => {
  console.log(more);
  console.log(bar);
  console.log(foo);
  return <div>woohoo</div>;
};

const EnhancedComponent = withFoo(withBar(WrappedComponent));

const App = () => <EnhancedComponent more="more" />;

const rootElement = document.getElementById("root");
render(<App />, rootElement);