@types/xxxx
包但遇到了问题。Definitions by:
中的index.d.ts
)所以他们可以回应。你好! 我刚刚更新到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-redux
的5.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
设置为以前的版本,一切都很好。
@variousauthors你能分享一下你正在使用的@types/react
和@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的清理带来的类型映射中。
最有用的评论
恢复到 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的清理带来的类型映射中。