0

I have a problem with React abstract generic class. When I declare one , there is missing prop on the child class and probably im doing the generic wrong. Main issue is

Type '{ moving: "no"; }' is not assignable to type 'Readonly<AbCarState & S>'

What i want to do : Make class that can be generic with default props, state and methods to extend it and create new class with possibility to change it.

import React from "react";
import { render } from "react-dom";

interface AbCarProps {
    color: string;
}
interface AbCarState {
    moving: string;
}

abstract class Car<P, S> extends React.Component<AbCarProps, AbCarState> {
    constructor(props: P & AbCarProps) {
        super(props);
        this.state = {
            moving: "no"
            
        }
    }
}

interface CarProps {
    tires: number;
}
interface CarState {
    movingFast: string;
}
class MyCar extends Car<CarProps, CarState> {
    state = {
        moving: "yes",
        movingFast: "no"
    };
    render() {
        return (
            <div>
                <div>movingFast : {this.state.movingFast}</div>
                <div>tires : {this.props.tires}</div>
                <div>color : {this.props.color}</div>
                <div>moving : {this.state.moving}</div>
            </div>
        );
    }
}

render(
    <MyCar tires={3} color={"red"} />,
    document.getElementById("root")
);

If I replace constuctor with some function then there is other or same issue:

abstract class Car<P, S> extends React.Component<AbCarProps & P, AbCarState & S> {
    move = ()=>{
        this.setState({moving:'yes'})
    }
}
Argument of type '{ moving: "yes"; }' is not assignable to parameter of type '(AbCarState & S) | ((prevState: Readonly<AbCarState & S>, props: AbCarProps & P) => (AbCarState & S) | Pick<...>) | Pick<...>'.
  Type '{ moving: "yes"; }' is not assignable to type 'Pick<AbCarState & S, "moving">'.
    Types of property 'moving' are incompatible.
      Type 'string' is not assignable to type 'string & S["moving"]'.
        Type 'string' is not assignable to type 'S["moving"]'

sanbox

So there is something im doing wrong. What could i do to make it work ? Thanks

mhan
  • 121
  • 1
  • 2
  • 5

1 Answers1

0

You are close. Just forget to pass P and S to component for reference in the abstract class.

Here is the codesandbox:

https://codesandbox.io/s/react-typescript-playground-forked-cm3oj?file=/src/index.tsx

abstract class Car<P, S> extends React.Component<
    P & AbCarProps, // passing P to be part of props
    S | AbCarState // passing S to be one of the states
> {
    constructor(props: P & AbCarProps) {
        super(props);
        this.state = {
            moving: "no"
        };
        console.log("Constructor");
    }
}
Mic Fung
  • 5,404
  • 2
  • 7
  • 17
  • Thanks. I have a question. If we put S | AbCarState then why there is no in MyCar? There is state with values{ moving: "yes", movingFast: "no"}. Why we could do both insted of one or (|) another ? – mhan May 16 '21 at 06:18
  • I don't know if i follow you. As MyCar state is actually independent to Car state. you can highlight MyCar state, the actual type is `{ moving: string; movingFast: string; }` instead of CarState so using `S | AbCarState` or `AbCarState` make no differences. If you want to enforce to enter both moving and movingFast in MyCar and fully make use of the parent state, you can see the updated codesandbox here: https://codesandbox.io/s/react-typescript-playground-forked-jtzxk?file=/src/index.tsx – Mic Fung May 16 '21 at 06:47