Rafał R
Rafał R

Reputation: 341

Initializing state in constructor in React Component that uses generic state

This is React + Typescript issue. I'm having a component that uses two generics for props and state.

interface Props {
    foo: string;
}
interface State {
    bar: string;
}
class Foo<
    P extends Props = Props,
    S extends State = State
> extends React.Component<P, S> {
    constructor(props: P) {
        super(props);

        this.state = { bar: "asd" };
    }
}

The assigment to this.state in constructor is found to be an error with message:

Type '{ bar: "asd"; }' is not assignable to type 'Readonly(S)'

When you remove the generic from class declaration and pass State interface directly to React.Component<P, State> the everything is OK.

My question is how to avoid such error ? I need to initialize state in such manner that will validate known state parameters (like bar in this example) but will allow to specify additional properties.

Upvotes: 0

Views: 2225

Answers (3)

Rafał R
Rafał R

Reputation: 341

As @Juraj Kocan wrote in his answer. The problem is created because:

you try to initialize generic type, this is wrong since you can extend this type and initialization won't be correct

Meanwhile I found that I can deal with it by spreading state in constructor:

interface Props {
    foo: string;
}
interface State {
    bar: string;
}
class Foo<
    P extends Props = Props,
    S extends State = State
> extends React.Component<P, S> {
    constructor(props: P) {
        super(props);

        this.state = { ...this.state, bar: "asd" };
    }
}

You can also do it without constructor:

interface Props {
    foo: string;
}
interface State {
    bar: string;
}
class Foo<
    P extends Props = Props,
    S extends State = State
> extends React.Component<P, S> {
   state:S = { ...this.state, bar: "asd" };
}

The latter solution was even better because spreading this.state in constructor made other errors appear where I used immutability-helper. Spreading in constructor made TypeScript lose track of index properties in update() function of immutability-helper.

Upvotes: 1

Juraj Kocan
Juraj Kocan

Reputation: 2878

you try to initialize generic type, this is wrong since you can extend this type and initialization won't be correct

example:

const instance = new Foo<Props, { otherRequiredProperty: string } & State>({ foo: '' });
cosnt otherProperty = instance.state.otherRequiredProperty

so i said that state type will have 2 required props but in your class you initialized with only bar...

you can create interface with required props and other optional if this is your point

interface Props {
  foo: string;
}

interface State {
  bar: string;
  [key: string]: string;
}

export class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: '',
    };

    this.setState({
      anything: '', // no error
    }); 
  }
}

this will not throw error and you can still add another property in to your state

Upvotes: 1

Isaac Suarez
Isaac Suarez

Reputation: 84

try this:

class Foo extends React.PureComponent<P, S> {
    state: State; //Add this line

    constructor(props: P) {
        super(props);

        this.state = { bar: "asd" };
    }
}

Upvotes: 0

Related Questions