skip to Main Content

I have a class with few parameters, that are initialized in the class constructor, and then I have a second class that extends the former. For instance (the number of parameters is actually much bigger):

class MyClass {
  name?: string;
  day?: Date;

  constructor({
    name,
    day,
  }: { name?: string, day?: Date } = {}) {
    this.name = name;
    this.day = day;
  }
}

class MyAwesomeClass extends MyClass {
  isAwesome?: boolean;

  constructor({
    isAwesome,
    ...props
  }: { isAwesome?: boolean } = {}) {
    super(props)
    this.isAwesome = isAwesome
  }
}

The problem with the above is that I am not specifying the type of props and, altough I am not getting a compiling error, I am not able to exploit coding suggestions and it is much more error prone.

const awesome = new MyAwesomeClass({ isAwesome: true, name: "test" })
// 'name' does not exist in type '{ isAwesome?: boolean | undefined; }'

What is the proper way to handle it? I may re-write all the parameters, but since I have plenty of them it is again very error prone, as I may miss a couple without noticing.

BTW, if your solution also helps with original MyClass, i.e. automatically extracting parameters instead of needing to rewrite them all, that would be great.

2

Answers


  1. One way is just to extract the props as a type, and then you can union the props on the inherited.

    eg.

    
    type MyClassProps = { name?: string, day?: Date };
    
    class MyClass {
      name?: string;
      day?: Date;
    
      constructor({
        name,
        day,
      }: MyClassProps = {}) {
        this.name = name;
        this.day = day;
      }
    }
    
    type MyAwesomeClassProps = MyClassProps & {isAwesome?: boolean}
    
    class MyAwesomeClass extends MyClass {
      isAwesome?: boolean;
    
      constructor({
        isAwesome,
        ...props
      }: MyAwesomeClassProps = {}) {
        super(props)
        this.isAwesome = isAwesome
      }
    }
    
    const c = new MyAwesomeClass({isAwesome:true, name:'Bob'});
    

    Working example
    TS Playground

    If you wish copy the type from the Constructor, you can do -> Is there a way to inherit parameters from a parent class's constructor? But personally I think that looks more complicated than just extracting the Types..

    And if using ConstructorParameters ->
    TS Playground

    I’ll leave for others to decide what is the easiest to implement / understand..

    Login or Signup to reply.
  2. Here is a helper type to let you do that:
    https://tsplay.dev/WvyDMm

    type Pure<T> = { [K in keyof T]: T[K] } & unknown
    type ExtendsParameters<C extends abstract new (...args: any) => any, P> = Pure<P & NonNullable<ConstructorParameters<C>[0]>>
    
    type x = ExtendsParameters<typeof MyClass, { isAwesome?: boolean }>
    // type x = {
    //     isAwesome?: boolean | undefined;
    //     name?: string | undefined;
    //     day?: Date | undefined;
    // }
    
    class MyAwesomeClass extends MyClass {
      isAwesome?: boolean;
    
      constructor({
        isAwesome,
        ...props
      }: ExtendsParameters<typeof MyClass, { isAwesome?: boolean }> = {}) {
        super(props)
        this.isAwesome = isAwesome
      }
    }
    

    It is possible to extend this for multiple arguments.
    If all of the class properties are its constructor parameters this may be simplified

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search