@types/xxxx
y tuve problemas.Definitions by:
en index.d.ts
) para que puedan responder.¡Hola! Acabo de actualizar a 5.0.16
e inmediatamente encontré algunos problemas al usar estas nuevas mecanografías en mi proyecto existente. A continuación, se muestran algunos números de versión relevantes:
@types/react-redux: ^5.0.16,
typescript: ^2.8.x,
react-redux: ^5.0.5,
Aquí hay un código que creo que debería compilarse (y que se compiló antes de 5.0.16
y que se compila de nuevo si volvemos a establecer la versión en 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 seguiría usando el contenedor así,
<ExampleContainer reportType={ReportType.USERS_CREATED} />
Sin embargo, con este código, usando el último @types/react-redux
en 5.0.16
y mecanografiado en 2.8.1
, obtengo el siguiente error:
[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>'.
Al inspeccionar los cambios introducidos en 5.0.16
, parece que el sistema de tipos está alterado porque connector
tiene el tipo InferableComponentEnhancerWithProps<IPropsFromState & IPropsFromParent, IPropsFromParent>
que significa que espera que el primer argumento sea del tipo IPropsFromState & IPropsFromParent
. A mi componente base le falta la propiedad reportType
.
Por ejemplo, si escribo esto:
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)
Estos ejemplos mínimos fallan. El primero falla por la misma razón que el código anterior, con el mismo mensaje de error opaco. El segundo falla porque, claramente, IExampleProps
no tiene una propiedad reportType: ReportType
por lo que no podemos asignar IExampleProps
a P
. Falta reportType
. Sospecho que esto es, bajo el capó, lo que está causando que falle el primer ejemplo.
¿Parece que después de 5.0.16
contenedores no pueden agregar nuevas propiedades a las interfaces de sus componentes empaquetados? Esto es lo que veo, mirando el código.
Es parte de mi flujo de trabajo normal definir los componentes en términos solo de las propiedades que necesitan, y luego extender esos componentes con HOC que definen una nueva interfaz y proporcionan al componente sus propiedades a través de la tienda redux. En algunos casos, la nueva interfaz es un subconjunto de la interfaz del componente envuelto, pero en otros casos puede que no lo sea. En el caso anterior, la nueva interfaz tiene una propiedad adicional reportType
que la persona que llama usará pero que el componente envuelto nunca verá.
Tengo otro ejemplo de una técnica que uso que está rota por este cambio:
Supongamos que tenemos dos componentes simples,
class NameDateComponent extends React.PureComponent<{ name: string, date: string}> {}
class DateOnlyComponent extends React.PureComponent<{ date: string }> {}
Quiero construir un HOC que proporcione date
para estos componentes sin necesidad de saber nada sobre la propiedad name
. De esta manera puedo hacer, por ejemplo, un NameProvider y un DateProvider, y componerlos así: NameProvider(DateProvider(BaseComponent))
o así DateProvider(NameProvider(BaseComponent))
, algo que normalmente no puedo hacer con un componente bien escrito. potenciador debido a la necesidad 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 nuevo potenciador de componentes independiente de la interfaz se utiliza de la siguiente manera:
const NameOnly = DateProvider(NameDateComponent)
const Nothing = DateProvider(DateOnlyComponent)
.
.
.
<Nothing />
<NameOnly name='Bob' />
<NameDateComponent name='Bob' date='2017-01-01' />
Por lo tanto, el DateProvider
HOC puede aumentar un componente sin ser plenamente consciente de la interfaz de ese componente. Solo necesita saber que el componente dado será capaz de aceptar los accesorios que proporciona.
Con 5.0.16
esto ya no funciona, en su lugar aparece el siguiente mensaje de error:
[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"'.
Jugando, he notado lo siguiente:
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
Cuando escribimos explícitamente el conector, ¿todo funciona?
¡Eso es todo! Gracias por leer;) por ahora he configurado mi @types/react-redux
a una versión anterior y todo está bien.
@variousauthors, ¿ podrías compartir las versiones de @types/react
y @types/react-redux
que estás usando?
EDITAR: el @
arruinó las cosas y los nombres de las bibliotecas no aparecieron, lo siento: decepcionado:
@variousauthors Creo que estás
¿Parece que después de 5.0.16 los contenedores no pueden agregar nuevas propiedades a las interfaces de sus componentes empaquetados? Esto es lo que veo, mirando el código.
El error es que las definiciones de tipo para la función connect
de redux se cruzan con ownProps
y mapPropsToState
. Esto significa que cualquier componente de "contenedor" se ve obligado a tener sus accesorios replicados en cualquier componente de "presentación" derivado, lo que rompe los patrones comunes de diseño de redux.
PR que introdujo: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24764
Otro problema: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/24922
Revertir a 5.0.15 no resuelve completamente el problema. Si bien 5.0.15 compilará este tipo de código sin previo aviso, todavía hay errores si agrega la verificación de tipo de los accesorios para el componente de presentación en la llamada connect
. (Suponiendo que su componente de envoltura tiene un accesorio que MyComponent
no tiene)
Por ejemplo, esto funcionará en 5.0.15, pero no en 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)
Sin embargo, incluso en la versión 5.0.15, no se pueden marcar las propiedades de MyComponent
sin un error del compilador.
connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
Esto generará un error al quejarse de que la propiedad de los componentes de envoltura no existe en MyComponent
Creo que este es un problema mayor que ha estado al acecho en las asignaciones de tipos que la limpieza de @Pajn sacó a la luz.
Comentario más útil
Revertir a 5.0.15 no resuelve completamente el problema. Si bien 5.0.15 compilará este tipo de código sin previo aviso, todavía hay errores si agrega la verificación de tipo de los accesorios para el componente de presentación en la llamada
connect
. (Suponiendo que su componente de envoltura tiene un accesorio queMyComponent
no tiene)Por ejemplo, esto funcionará en 5.0.15, pero no en 5.0.16
connect(stateToProps, dispatchToProps)(MyComponent)
Sin embargo, incluso en la versión 5.0.15, no se pueden marcar las propiedades de
MyComponent
sin un error del compilador.connect<statetoprops, dispatchtoprops, mycomponentprops)(stateToProps, dispatchToProps)(MyComponent)
Esto generará un error al quejarse de que la propiedad de los componentes de envoltura no existe en
MyComponent
Creo que este es un problema mayor que ha estado al acecho en las asignaciones de tipos que la limpieza de @Pajn sacó a la luz.