使用 recompose 和 typescript 正确键入 HigherOrderComponents
Posted
技术标签:
【中文标题】使用 recompose 和 typescript 正确键入 HigherOrderComponents【英文标题】:Correct Typing for HigherOrderComponents with recompose and typescript 【发布时间】:2019-05-30 05:09:08 【问题描述】:我目前正在尝试重新组合到我的反应代码库中。因此,我试图让一些基本的东西正常工作,并且我得到了它的工作,但我不确定这是否是 recompose 的正确工作方式。
所以我有以下代码:
interface Value
value: string
interface Loading
loading: boolean
const TestComponent = (props: Value) => <div>Value: props.value</div>
const LoadingComponent = () => <div>Loading ...</div>
所以我有一个 TestComponent,它应该显示道具中提供的值,另外我还有一个 LoadingComponent,它应该在设置加载道具时显示。
所以我使用了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>
返回 ComponentEnhancer<any,TOutter>
。据我所知,我希望能够提供any
组件,而<TOutter>
泛型就是这样,生成的组件知道测试功能所需的道具。这也可以在没有额外道具的情况下工作。
但是 ComponentEnhancer 的 TypeDefinition 看起来像这样(重构 0.30.2):
interface ComponentEnhancer<TInner, TOutter>
(component: Component<TInner>): ComponentClass<TOutter>
所以,我从之前的branch
函数收到的ComponentEnhancer<any, Loading>
将返回一个ComponentClass<Loading>
。但是,我提供给 ComponentEnhancer 的组件的 <TInner>
将被丢弃,并且我无法在增强组件中使用我的 Value
道具。
所以我的问题是,我做错了吗,有没有更好的方法来实现这一点(使用重组)。或者它只是 TypeDeclaration 中的一个错误,因为将 ComponentEnhancer
的返回更改为 ComponentClass<TOutter & TInner>
为我解决了整个问题。
对此有什么想法吗?
【问题讨论】:
【参考方案1】:我对@987654321@ 和recompose
不熟悉,但如果这只是打字问题,我们可以解决。
问题是branch
的类型不是很好。如果意图是将包装组件的属性转发到 HOC 并将显式键入到 branch
的道具添加到 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 />
【讨论】:
嗯,这基本上就是我在最后两句话中所写的。将返回类型更改为ComponentClass<TInner & TOutter>
时,它将起作用。但是我对 as unknown
以及为什么 BetterComponentEnhancer 参数前面的 <TInner>
感到困惑
@Azael wops,我的错误错过了问题的最后一行,我将BetterComponentEnhancer
设为通用函数以从传入的组件(在本例中为 TestComponent)@ 987654331@ 只是一个双重类型断言,因为 TS 不允许我直接断言到 BetterComponentEnhancer
所以我尝试了您的解决方案,它确实有效。工具提示显示React.ComponentClass<Value & Loading, any>
。对于解决方案,它是React.ComponentClass<any, any>
。那里的 TS 语法仍然有点困惑。我将在 @types/recompose 存储库上打开一个问题,并参考您的解决方案。
@Azael 语法只是一个通用函数签名,它是非常标准的 TS。如果您打开问题,也请在此处发布,我很想看看团队的回复
遗憾的是,我仍然没有对该问题的答案......但是我正在使用您的解决方案,所以我将您的答案标记为正确。以上是关于使用 recompose 和 typescript 正确键入 HigherOrderComponents的主要内容,如果未能解决你的问题,请参考以下文章
如何在 reactjs 中使用 recompose 清理表单?
[Recompose] Lock Props using Recompose -- withProps
你如何从 recompose 中读取这个 curry'd 函数……我的脑痛
使用 recompose setStatic 并将其连接到 redux (nextjs)
[Recompose] Transform Props using Recompose --mapProps
[Recompose] Add Lifecycle Hooks to a Functional Stateless Component using Recompose