I'm using an abstract class as a parent React component that a number of child components extend in my application.
It has a default state as well as a couple of class methods that can optionally be overwritten from an extending component.
The abstract component looks like this:
import * as React from 'react';
export interface StatefulProps {
observers?: Array<(context: any, state: object) => any>;
}
export interface StatefulState {
machine: {
[propName: string]: any;
};
name: string;
value?: any;
}
export default abstract class StatefulComponent<P extends StatefulProps, S extends StatefulState> extends React.Component<P, S> {
static defaultProps = {
observers: [],
};
state = {
machine: {},
name: '',
};
abstract generateState(stateName: string, stateParam?: any): object;
shouldComponentUpdate(_: P, nextState: S) {
const { name } = this.state;
if (nextState && nextState.name !== name) {
return true;
}
return false;
}
goToState = (stateName: string, stateParam?: any) => {
const { observers } = this.props;
this.setState(
{
name: stateName,
machine: this.generateState(stateName, stateParam),
},
() => {
if (observers && observers.length) {
observers.forEach(observer => {
if (observer instanceof Function) {
observer(this, this.state);
}
});
}
}
);
}
}
Yet state is underlined by tslint which displays the following error on hover:
Property 'state' in type 'StatefulComponent<P, S>' is not assignable to the same property in base type 'Component<P, S, any>'.
Type '{ machine: {}; name: string; }' is not assignable to type 'Readonly<S>'.
I can't figure out for the life of me why it's complaining...
I'm fairly new to TypeScript so it's completely plausible I'm being a total muppet in the way I'm doing things but any advice/help would be much appreciated!
You can't assign an object literal to something that is of a generic type. The generic type constrain defines the minimal set of properties that S
can have, but it could have more properties, some of them mandatory, consider this derived class:
class MyStatefulComponent extends StatefulComponent<StatefulProps, StatefulState & { newMandatoryProp: number }> {
constructor(p: StatefulProps){
super(p);
this.state.newMandatoryProp // will be undefined beacuse the base class did not initailize it correctly
}
generateState(stateName: string, stateParam?: any): object { return null as any }
}
You can use a type assertion to do this anyway but it is not type safe:
export default abstract class StatefulComponent<P extends StatefulProps, S extends StatefulState> extends React.Component<P, S> {
constructor(props: P) {
super(props);
this.state = {
machine: {},
name: '',
} as S;
}
}
You could also have an abstract method for creating the state, and pass responsibility of creating the full state object to the derived class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With