使用 recompose 和 typescript 正确输入 HigherOrderComponents

Correct Typing for HigherOrderComponents with recompose and typescript

我目前正在尝试将其重组到我的 React 代码库中。因此,我试图让一些基本的东西起作用,并且我让它起作用了,但我不太确定,如果这是重新组合的正确工作方式。

所以我有以下代码:

interface Value {
  value: string
}

interface Loading {
  loading: boolean
}

const TestComponent = (props: Value) => <div>Value: {props.value}</div>
const LoadingComponent = () => <div>Loading ...</div>

所以我有一个 TestComponent,它应该显示 props 中提供的值,另外我还有一个 LoadingComponent,它应该在设置加载 props 时显示。

所以我使用了recompose的branch功能

const withLoading = branch<Loading>(
  ({loading}) => loading, 
  renderComponent(LoadingComponent)
) 

现在,当我在 任何没有道具的组件上使用 withLoading 我可以在它们上设置加载道具。

const EnhancedCompWithLoading = withLoading(SomeCompWithoutProps)

render() {
  return <EnhancedCompWithLoading loading={this.state.loading} />
}

这对我来说很好,真正的问题是在尝试将它与带有道具的组件一起使用时开始。当我这样尝试时:

const TestWithLoading = withLoading(TestComponent)

render() {
  return <TestWithLoading value="testVal" loading={this.state.loading} />
}

我收到错误消息 TS2339: Property 'value' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Loading, any, any>> & Readonly<{ children?: ReactNode; }> & Readonly<Loading>'.

所以我查看了@types/recompose 中的类型定义。 branch<TOutter> return 一个 ComponentEnhancer<any,TOutter>。据我所知,我希望能够提供 any 组件,而 <TOutter> 泛型就是这样,因此生成的组件知道测试功能所需的道具。那也可以在没有附加道具的情况下工作。

但是 ComponentEnhancer 的 TypeDefinition 看起来像这样(重构 0.30.2):

interface ComponentEnhancer<TInner, TOutter> {
  (component: Component<TInner>): ComponentClass<TOutter>
}

因此,我从之前的 branch 函数收到的 ComponentEnhancer<any, Loading> 将 return 变成 ComponentClass<Loading>。但是,我提供给 ComponentEnhancer 的组件的 <TInner> 将被丢弃,我无法在增强组件中使用我的 Value 道具。

所以我的问题是,我是不是做错了,有没有更好的方法来实现这个(通过重组)。或者它只是 TypeDeclaration 中的一个错误,因为将 ComponentEnhancer 的 return 更改为 ComponentClass<TOutter & TInner> 为我修复了整个问题。 对此有什么想法吗?

我不熟悉 branchrecompose,但如果这只是打字问题,我们可以解决。

问题是 branch 的类型不是很好。如果目的是将包装组件的属性转发到 HOC 并将显式输入 branch 的 props 添加到 HOC,那么更好的结果类型是:

type BetterComponentEnhancer<TOutter> = {
    <TInner>(component: React.ComponentType<TInner>): React.ComponentClass<TInner & TOutter>
}

对于这种类型,这将起作用:

const withLoading = branch<Loading>(
    ({ loading }) => loading,
    renderComponent(LoadingComponent)
)as unknown as BetterComponentEnhancer<Loading>

type BetterComponentEnhancer<TOutter> = {
    <TInner>(component: React.ComponentType<TInner>): React.ComponentClass<TInner & TOutter>
}

const TestWithLoading = withLoading(TestComponent)

function render() {
    return <TestWithLoading value="testVal" loading={true} />
}