React Recompose 在 Props 上导致 Typescript 错误

Posted

技术标签:

【中文标题】React Recompose 在 Props 上导致 Typescript 错误【英文标题】:React Recompose Causing Typescript Error On Props 【发布时间】:2019-03-30 01:49:22 【问题描述】:

我有一个非常基本的有状态组件,我使用 recompose 将多个 HOC 添加到我的组件中(在我的示例中,为了简单起见,我只使用一个)。出于某种原因,打字稿给了我一个关于我的道具进入我的组件的错误。我怎样才能摆脱这个错误?

这是我的代码:

import * as React from 'react';
import  connect  from 'react-redux';
import  compose  from 'recompose';

interface IStoreState 
  readonly sessionState: 
    authUser:  email: string; 
  


interface IAccountPageProps  
  authUser:  email: string  


const AccountPage = ( authUser : IAccountPageProps ) =>
    <div>
      <h1>Account: authUser.email</h1>
    </div>

const mapStateToProps = (state: IStoreState) => (
  authUser: state.sessionState.authUser,
);

export default compose(
  connect(mapStateToProps)
)(AccountPage);

我得到的错误是:

Argument of type '( authUser : IAccountPageProps) => Element' is not assignable to parameter of type 'ComponentType<>'.
  Type '( authUser : IAccountPageProps) => Element' is not assignable to type 'StatelessComponent<>'.
    Types of parameters '__0' and 'props' are incompatible.
      Type ' children?: ReactNode; ' is not assignable to type 'IAccountPageProps'.
        Property 'authUser' is missing in type ' children?: ReactNode; '.

如果我不使用 recompose 而是写

export default connect(mapStateToProps)(AccountPage)

我没有收到任何错误。

【问题讨论】:

【参考方案1】:

The current typing of compose 非常没用。如果要使用compose,则必须手动指定原始组件和最终组件的props 类型,并且不会检查您指定的类型是否与您传递的高阶组件列表匹配:

export default compose<IAccountPageProps, >(
  connect(mapStateToProps)
)(AccountPage);

我建议不要在 TypeScript 中使用 compose

【讨论】:

好的,再次感谢马特。那么显而易见的问题是,您使用什么来代替 Typescript 撰写?我的生产导出如下所示,其中withAuthorization 是我用来验证是否有人登录的 HOC。export default compose(withAuthorization(authCondition), connect(mapStateToProps))(AccountPage); @JonLamb 你找到这个 HOC 链接的优雅解决方案了吗? @bert 我所知道的最佳选择是按照 Brian 的回答中所示的顺序应用 HOC。 谢谢老兄,有道理! 或更好 -> 不要使用打字稿【参考方案2】:

compose 的类型允许您指定生成的组件的类型以及可以调用它的组件的类型,这样可以避免错误:

export default compose<IAccountPageProps, >(
  connect(mapStateToProps)
)(AccountPage);

不幸的是,compose 无法确保传递给它的函数的类型安全或兼容性。

因此,例如,即使明显无效,也不会产生打字错误:

export default compose<IAccountPageProps, >(
  connect(mapStateToProps),
  () => 'compose typing allows any function'
)(AccountPage);

嵌套 HOC 调用更安全:

export default 
connect(mapStateToProps)(
  firstHoc(
    secondHoc(
      AccountPage
    )
  )
);

【讨论】:

谢谢 Brian,你用什么代替 compose 来连接 Typescript 中的多个 HOC? 我最终嵌套了 HOC 调用。它不如 compose 风格的方法漂亮,但类型安全是值得的。 @JonLamb 我用答案更新了我的回复,一个演示 compose 问题的示例,以及推荐的方法【参考方案3】:

我发现的最佳解决方案是重新声明类型,以便您也获得connect 的类型(包括可用于轻松测试的.WrappedComponent),否则默认为React.ComponentClass&lt;, any&gt;这是不正确的,并且没有按照上面@Matt 的示例为您提供有关组件的信息。

使用您的示例,请参阅:

import React,  SFC  from 'react';
import  connect, ConnectedComponentClass  from 'react-redux';

export default compose<IAccountPageProps, >(
  connect(mapStateToProps)
)(AccountPage) as ConnectedComponentClass<SFC<IAccountPageProps>, >

现在,当您在其他地方使用该组件时,返回的组件在连接包装器中的正确类型为 ConnectedComponentClass&lt;React.StatelessComponent&lt;IAccountPageProps&gt;, &gt;,现在您还可以访问 connectedComponent 值,例如 .WrappedComponent

ConnectedComponentClass 是 react-redux 的类型,因为它是连接组件的最终类型。在一个完美的世界里,这不应该是必要的,但它确实有效。

【讨论】:

不,这不是一个好的解决方案。这只是对更准确类型的类型断言。核心问题是compose 的输入是不安全的,除非改进输入,否则应该避免在 TypeScript 中使用compose 好吧,避免使用是一个解决方案,但我真的认为这也是一个解决方案。我们都知道这种缺失,并且类型断言修复了它。导出的函数有一个安全的类型,为了消费者这很好。然后,在内部,通过断言(这是一种 hack)获得正确的签名。还要考虑ConnectedComponentClass 与该签名充分重叠(不需要as unknown)。这也是可扩展的,因为在我们修复 compose 定义时,您只需要删除断言。

以上是关于React Recompose 在 Props 上导致 Typescript 错误的主要内容,如果未能解决你的问题,请参考以下文章

[Recompose] Stream Props to React Children with RxJS

[Recompose] Make Reusable React Props Streams with Lenses

[Recompose] Lock Props using Recompose -- withProps

[Recompose] Create Stream Behaviors to Push Props in React Components with mapPropsStream

[Recompose] Transform Props using Recompose --mapProps

如何在 reactjs 中使用 recompose 清理表单?