@types/xxxx
e tive problemas.Definitions by:
em index.d.ts
) para que eles possam responder.Olá! Acabei de atualizar para 5.0.16
e imediatamente encontrei alguns problemas ao usar essas novas tipificações em meu projeto existente. Aqui estão alguns números de versões relevantes:
@types/react-redux: ^5.0.16,
typescript: ^2.8.x,
react-redux: ^5.0.5,
Aqui está algum código que eu acho que deveria compilar (e que foi compilado antes de 5.0.16
e que compila novamente se definirmos a versão de volta para 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
Normalmente, eu usaria o contêiner assim,
<ExampleContainer reportType={ReportType.USERS_CREATED} />
No entanto, com este código, usando o @types/react-redux
mais recente em 5.0.16
e datilografado em 2.8.1
, recebo o seguinte erro:
[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>'.
Inspecionando as mudanças introduzidas em 5.0.16
, parece que o sistema de tipos está chateado porque connector
tem o tipo InferableComponentEnhancerWithProps<IPropsFromState & IPropsFromParent, IPropsFromParent>
que significa que ele espera que o primeiro argumento seja do tipo IPropsFromState & IPropsFromParent
. Meu componente básico está faltando a propriedade reportType
.
Por exemplo, se eu escrever isto:
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)
Esses exemplos mínimos falham. O primeiro falha pelo mesmo motivo do código acima, com a mesma mensagem de erro opaca. A segunda falha porque, claramente, IExampleProps
não tem uma propriedade reportType: ReportType
então não podemos atribuir IExampleProps
a P
. Está faltando reportType
. Suspeito que seja isso, nos bastidores, o que está causando o fracasso do primeiro exemplo.
Parece que depois de 5.0.16
contêineres não têm permissão para adicionar novas propriedades às interfaces de seus componentes embalados? Isso é o que vejo, olhando para o código.
É parte do meu fluxo de trabalho normal definir componentes em termos apenas de quais propriedades eles precisam e, em seguida, estender esses componentes com HOCs que definem uma nova interface e fornecem ao componente suas propriedades por meio do armazenamento redux. Em alguns casos, a nova interface é um subconjunto da interface do componente empacotado, mas em outros casos pode não ser. No caso acima, a nova interface tem uma propriedade adicional reportType
que o chamador usará, mas que o componente empacotado nunca verá.
Eu tenho outro exemplo de uma técnica que uso que é interrompida por esta mudança:
Suponha que temos dois componentes simples,
class NameDateComponent extends React.PureComponent<{ name: string, date: string}> {}
class DateOnlyComponent extends React.PureComponent<{ date: string }> {}
Quero construir um HOC que forneça date
para esses componentes sem precisar saber nada sobre a propriedade name
. Desta forma, posso fazer, por exemplo, um NameProvider e um DateProvider, e compô-los assim: NameProvider(DateProvider(BaseComponent))
ou assim DateProvider(NameProvider(BaseComponent))
, algo que normalmente não posso fazer com um componente bem digitado realçador devido à necessidade de especificar 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
}
Este novo intensificador de componente agnóstico de interface é usado da seguinte maneira:
const NameOnly = DateProvider(NameDateComponent)
const Nothing = DateProvider(DateOnlyComponent)
.
.
.
<Nothing />
<NameOnly name='Bob' />
<NameDateComponent name='Bob' date='2017-01-01' />
Portanto, o DateProvider
HOC é capaz de aumentar um componente sem estar totalmente ciente da interface desse componente. Ele só precisa saber que determinado componente será capaz de aceitar os adereços que fornece.
Com 5.0.16
isso não funciona mais; em vez disso, recebo a seguinte mensagem de erro:
[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"'.
Brincando, percebi o seguinte:
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
Quando digitamos explicitamente o conector, tudo funciona?
É isso! Obrigado por ler;) por agora eu configurei meu @types/react-redux
para uma versão anterior e está tudo bem.
@variousauthors poderia compartilhar as versões de @types/react
e @types/react-redux
você está usando?
EDIT: o @
bagunçou as coisas e os nomes das libs não apareceram, desculpe: desapontado:
@variousauthors , acho que você
Parece que após o 5.0.16 os contêineres não têm permissão para adicionar novas propriedades às interfaces de seus componentes embalados? Isso é o que vejo, olhando para o código.
O bug é que as definições de tipo para a função connect
do redux cruzam ownProps
e mapPropsToState
. Isso significa que qualquer componente de "contêiner" é forçado a ter seus adereços replicados em qualquer componente de "apresentação" derivado - o que quebra os padrões de design redux comuns.
PR que introduziu: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24764
Outro problema: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/24922
Reverter para 5.0.15 não resolve completamente o problema. Embora 5.0.15 compile este tipo de código sem avisar, ainda haverá erros se você adicionar a verificação de tipo dos adereços para o componente de apresentação na chamada connect
. (Supondo que seu componente de embrulho tenha um suporte que MyComponent
não tem)
Por exemplo, isso funcionará em 5.0.15, mas não 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)
No entanto, mesmo no 5.0.15, você não pode verificar os MyComponent
props sem erro do compilador
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
Haverá um erro reclamando que a propriedade dos componentes da embalagem não existe em MyComponent
Acho que esse é um problema maior que está escondido nos mapeamentos de tipo que a limpeza de @Pajn trouxe à luz.
Comentários muito úteis
Reverter para 5.0.15 não resolve completamente o problema. Embora 5.0.15 compile este tipo de código sem avisar, ainda haverá erros se você adicionar a verificação de tipo dos adereços para o componente de apresentação na chamada
connect
. (Supondo que seu componente de embrulho tenha um suporte queMyComponent
não tem)Por exemplo, isso funcionará em 5.0.15, mas não 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)
No entanto, mesmo no 5.0.15, você não pode verificar os
MyComponent
props sem erro do compiladorconnect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
Haverá um erro reclamando que a propriedade dos componentes da embalagem não existe em
MyComponent
Acho que esse é um problema maior que está escondido nos mapeamentos de tipo que a limpeza de @Pajn trouxe à luz.