Definitelytyped: 一些现有的连接调用被 5.0.16 中断

创建于 2018-04-11  ·  3评论  ·  资料来源: DefinitelyTyped/DefinitelyTyped

  • [x] 我尝试使用@types/xxxx包但遇到了问题。
  • [x] 我尝试使用最新的稳定版 tsc。 https://www.npmjs.com/package/typescript
  • [x] 我有一个不适合StackOverflow 的问题。 (请在那里提出任何适当的问题)。
  • [x] [提及](https://github.com/blog/821-mention-somebody-they-re-notified) 作者(见Definitions by:中的index.d.ts )所以他们可以回应。

    • 作者: @Pajn @tkqubo @thasner @kenzierocks @clayne11 @tansongyang @NicholasBoll @mDibyo @pdeva

问题)

你好! 我刚刚更新到5.0.16并立即在我现有的项目中使用这些新类型时遇到了一些麻烦。 以下是一些相关的版本号:

@types/react-redux: ^5.0.16,
typescript: ^2.8.x,
react-redux: ^5.0.5,

典型的容器不编译

这是我认为应该编译的一些代码(并且在5.0.16之前确实编译过,如果我们将版本设置回5.0.15再次编译):

// components/exampleComponent.ts
import React = require('react')
import { IState, IDateRange, ReportType } from '../../types/index'

export interface IExampleProps {
  label?: string
  dateRange: IDateRange
  onDateRangeChange: (value: IDateRange) => void
}

export class ExampleComponent extends React.Component<IExampleProps> {
  // implementation
}
// containers/exampleComponent.ts
import { connect } from 'react-redux'
import { IState, IDateRange, ReportType } from '../../types/index'

interface IPropsFromState {
  dateRange: IDateRange
}

interface IPropsFromParent {
  reportType: ReportType
}

const mapStateToProps = (state: IState, props: IPropsFromParent): IPropsFromState => {
  const config = state.reporting.reportConfigs[props.reportType]

  return {
    dateRange: config ? config.dateRange : null,
  }
}

const connector = connect<IPropsFromState, null, IPropsFromParent>(
  mapStateToProps,
)

export const ExampleContainer = connector(ExampleComponent) // <-- this does not compile

通常我会继续使用这样的容器,

<ExampleContainer reportType={ReportType.USERS_CREATED} />

然而,与此代码,采用最新的@types/react-redux5.0.16在和打字稿2.8.1我反而得到以下错误:

[ts]
Argument of type 'typeof ExampleComponent' is not assignable to parameter of type 'ComponentType<IPropsFromState & DispatchProp<any> & IPropsFromParent>'.
  Type 'typeof ExampleComponent' is not assignable to type 'StatelessComponent<IPropsFromState & DispatchProp<any> & IPropsFromParent>'.
    Type 'typeof ExampleComponent' provides no match for the signature '(props: IPropsFromState & DispatchProp<any> & IPropsFromParent & { children?: ReactNode; }, context?: any): ReactElement<any>'.

检查5.0.16引入的更改,感觉类型系统很混乱,因为connector类型InferableComponentEnhancerWithProps<IPropsFromState & IPropsFromParent, IPropsFromParent> ,这意味着它期望第一个参数的类型IPropsFromState & IPropsFromParent 。 我的基本组件缺少属性reportType

例如,如果我这样写:

const f = <P extends IPropsFromParent & IPropsFromState>(component: Component<P>) => { /**/ }
const g = <P extends IPropsFromParent & IPropsFromState>(props: P) => { /**/ }

const record: IExampleProps = null

f(ExampleComponent)
g(record)

这些最小的例子失败了。 第一个失败的原因与上面的代码相同,并带有相同的不透明错误消息。 第二个失败,因为显然IExampleProps没有属性reportType: ReportType所以我们不能将IExampleProps分配给P 。 它缺少reportType 。 我怀疑这是导致第一个示例失败的原因。

5.0.16容器之后似乎不允许向其包装组件的接口添加新属性? 这是我看到的,看着代码。

这是我正常工作流程的一部分,仅根据组件需要的属性来定义组件,然后使用 HOC 扩展这些组件,这些 HOC 定义新接口并通过 redux 存储为组件提供其属性。 在某些情况下,新接口是包装组件接口的子集,但在其他情况下可能不是。 在上面的例子中,新接口有一个额外的属性reportType ,调用者将使用它,但被包装的组件永远不会看到。


更特殊的容器无法编译

我有一个我使用的技术的另一个例子,它被这个变化破坏了:

假设我们有两个简单的组件,

class NameDateComponent extends React.PureComponent<{ name: string, date: string}> {}

class DateOnlyComponent extends React.PureComponent<{ date: string }> {}

我想构建一个为这些组件提供date的 HOC,而根本不需要了解有关name属性的任何信息。 通过这种方式,我可以制作一个 NameProvider 和一个 DateProvider,然后像这样组合它们: NameProvider(DateProvider(BaseComponent))或像这样DateProvider(NameProvider(BaseComponent)) ,这是我通常无法使用类型良好的组件做的事情增强器,因为需要指定TOwnProps

interface IWithDate {
  date: string
}

// ComponenProps defines a component interface that includes IWithDate
// NewAPI defines a new interface that excludes IWithDate
// so the types represent only what will change in the given component, without needing to know the rest of the interface
function DateProvider <ComponentProps extends IWithDate, NewAPI extends Minus<ComponentProps, IWithDate>> (Base: CompositeComponent<ComponentProps>) {

  const enhancer = connect<ComponentProps, null, NewAPI>(
    (_state: IState, props: NewAPI): ComponentProps => {
      // because of the 'never' type, 
      // typescript doesn't think NewAPI & IWithProps == ComponentProps
      // so we have to coerce this (but you and I can see that the above holds)
      const newProps: any = Object.assign({
        date: '2017-01-01'
      }, props)

      return newProps as ComponentProps
    },
    null
  )

  return enhancer(Base) // <-- after 5.0.16 this line does not compile
}

这个新的接口不可知组件增强器的使用如下:

const NameOnly = DateProvider(NameDateComponent)
const Nothing = DateProvider(DateOnlyComponent)

.
.
.

<Nothing />
<NameOnly name='Bob' />
<NameDateComponent name='Bob' date='2017-01-01' />

因此, DateProvider HOC 能够在不完全了解该组件接口的情况下增强该组件。 它只需要知道给定的组件能够接受它提供的道具。

使用5.0.16这不再有效,而是我收到以下错误消息:

[ts]
Argument of type 'CompositeComponent<ComponentProps>' is not assignable to parameter of type 'ComponentType<ComponentProps & NewAPI>'.
  Type 'ComponentClass<ComponentProps>' is not assignable to type 'ComponentType<ComponentProps & NewAPI>'.
    Type 'ComponentClass<ComponentProps>' is not assignable to type 'StatelessComponent<ComponentProps & NewAPI>'.
      Types of property 'propTypes' are incompatible.
        Type 'ValidationMap<ComponentProps>' is not assignable to type 'ValidationMap<ComponentProps & NewAPI>'.
          Type 'keyof ComponentProps | keyof NewAPI' is not assignable to type 'keyof ComponentProps'.
            Type 'keyof NewAPI' is not assignable to type 'keyof ComponentProps'.
              Type 'keyof NewAPI' is not assignable to type '"date"'.

在玩的过程中,我注意到以下几点:

interface IWithDate {
  date: string
}

interface IComponentProps {
  name: string
  date: string
}

// I've torn out the internals of the DateProvider above
const connect1 = <T extends IWithDate, U extends Omit<T, keyof IWithDate>>(base: CompositeComponent<T>) => {
  const enhancer: InferableComponentEnhancerWithProps<T & U, U> = () => null

  return enhancer(base) // <-- this is a compile error
}

const connect2 = <T extends IWithDate, U extends Omit<T, keyof IWithDate>>() => {
  const enhancer: InferableComponentEnhancerWithProps<T & U, U> = () => null

  return enhancer
}

const enhancer = connect2<IComponentProps, Omit<IComponentProps, keyof IWithDate>>()
const container = enhancer(NameDateComponent) // <-- this is not a compile error

当我们明确键入连接器时,一切正常吗?


就是这样! 感谢阅读 ;) 现在我已经将@types/react-redux设置为以前的版本,一切都很好。

最有用的评论

恢复到 5.0.15 并不能完全解决问题。 虽然 5.0.15 会在没有警告的情况下编译这种代码 - 如果您在connect调用中为表示组件添加类型检查,它仍然会出错。 (假设你的包装组件有一个MyComponent没有的道具)

例如,这将适用于 5.0.15,但不适用于 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)

但是 - 即使在 5.0.15 中,您也无法在没有编译器错误的情况下对MyComponent道具进行类型检查
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
这将错误地抱怨MyComponent中不存在包装组件道具

我认为这是一个更大的问题,它一直潜伏在@Pajn的清理带来的类型映射中。

所有3条评论

@variousauthors你能分享一下你正在使用的@types/react@types/react-redux吗?

编辑: @搞砸了,因为库的名称没有出现,抱歉:失望:

@variousauthors我认为你对这条评论很

似乎在 5.0.16 之后容器不允许向其包装组件的接口添加新属性? 这是我看到的,看着代码。

错误在于 redux 的connect函数的类型定义ownPropsmapPropsToState相交。 这意味着任何“容器”组件都被迫在任何派生的“展示性”组件中复制其道具——这打破了常见的 redux 设计模式。

引入的公关: https :
其他问题: https :

恢复到 5.0.15 并不能完全解决问题。 虽然 5.0.15 会在没有警告的情况下编译这种代码 - 如果您在connect调用中为表示组件添加类型检查,它仍然会出错。 (假设你的包装组件有一个MyComponent没有的道具)

例如,这将适用于 5.0.15,但不适用于 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)

但是 - 即使在 5.0.15 中,您也无法在没有编译器错误的情况下对MyComponent道具进行类型检查
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
这将错误地抱怨MyComponent中不存在包装组件道具

我认为这是一个更大的问题,它一直潜伏在@Pajn的清理带来的类型映射中。

此页面是否有帮助?
0 / 5 - 0 等级