@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, которые определяют новый интерфейс и предоставляют компоненту его свойства через хранилище redux. В некоторых случаях новый интерфейс является подмножеством интерфейса обернутого компонента, но в других случаях это может быть не так. В приведенном выше случае новый интерфейс имеет дополнительное свойство reportType
которое вызывающий будет использовать, но который обернутый компонент никогда не увидит.
У меня есть еще один пример техники, которую я использую, которая не работает из-за этого изменения:
Предположим, у нас есть два простых компонента,
class NameDateComponent extends React.PureComponent<{ name: string, date: string}> {}
class DateOnlyComponent extends React.PureComponent<{ date: string }> {}
Я хочу создать HOC, который предоставляет date
для этих компонентов без необходимости вообще ничего знать о свойстве name
. Таким образом я могу создать, например, NameProvider и DateProvider, и составить их так: NameProvider(DateProvider(BaseComponent))
или как этот DateProvider(NameProvider(BaseComponent))
, то, что я обычно не могу сделать с хорошо типизированным компонентом Enhancer из-за необходимости указывать 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
вы используете?
РЕДАКТИРОВАТЬ: @
испортил вещи, а имена библиотек не появились, извините: разочарован:
@variousauthors Я думаю, вы попали в точку с этим комментарием
Похоже, что после 5.0.16 контейнерам не разрешено добавлять новые свойства к интерфейсам их обернутых компонентов? Вот что я вижу, глядя на код.
Ошибка в том, что определения типов для функции redux connect
пересекаются с ownProps
и mapPropsToState
. Это означает, что любой компонент «контейнер» вынужден реплицировать свои свойства в любых производных «презентационных» компонентах, что нарушает общие шаблоны проектирования redux.
PR, который представил: https://github.com/DefinitiTyped/DefinitiTyped/pull/24764
Другая проблема: https://github.com/DefinitiTyped/DefinitiTyped/issues/24922
Возврат к 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 .