How can I do constructor overloading in a derived class in TypeScript?
Asked Answered
P

1

8

Assuming I have a 'base' class such as this:

class CcDefinition {
  // Some properties here

  constructor (json: string);
  constructor (someVar: number, someOtherVar: string);
  constructor (jsonOrSomeVar: any, someOtherVar?: string) {
    if (typeof jsonOrSomeVar=== "string") {
      // some JSON wrangling code here
    } else {
      // assign someVar and someOtherVar to the properties
    }
  }
}

I want to be able to extend this base class while still supporting constructor overloading. For example:

class CcDerived extends CcDefinition {
  // Some additional properties here

  constructor (json: string);
  constructor (someVar: boolean, someOtherVar: number, someAdditionalVar: string);
  constructor (jsonOrSomeVar: any, someOtherVar?: number, someAdditionalVar?: string) {
    if (typeof jsonOrSomeVar=== "string") {
      super.constructFromJson(jsonOrSomeVar);
    } else {
      super.constructFromDef(someOtherVar, someAdditionalVar);
      // assign someVar to the additional properties of this derived class
    }
  }
}

The problem is that Typescript demands that the 'super' keyword appear first (literally) in the constructor implementation. The specific build error message is:

"A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties."

However, I need to determine which parameters I will pass into the 'super' (i.e. use a different constructor overload) based upon what was supplied to the extended (derived) class. You should assume here that the derived class' constructor overloads may be very different from the super's.

Is there a workaround for what I'm trying to achieve?

Patchwork answered 2/10, 2014 at 5:45 Comment(1)
Here's someone raising the same issue (without resolution): [typescript.codeplex.com/workitem/91]Patchwork
L
7

This restriction only applies if you have initialized member properties in the derived class, so the first workaround is to simply only declare those properties and then initialize them in the derived class constructor.

In other words, you can change:

class CcDerived extends CcDefinition {
  y = 10;

  constructor (json: string);
  constructor (someVar: boolean, someOtherVar: number, someAdditionalVar: string);
  constructor (jsonOrSomeVar: any, someOtherVar?: number, someAdditionalVar?: string) {
    if (typeof jsonOrSomeVar=== "string") {
      super(jsonOrSomeVar);
    } else {
      super(someOtherVar, someAdditionalVar);
    }
  }
}

to this:

class CcDerived extends CcDefinition {
  // Some additional properties here
  y: number;

  constructor (json: string);
  constructor (someVar: boolean, someOtherVar: number, someAdditionalVar: string);
  constructor (jsonOrSomeVar: any, someOtherVar?: number, someAdditionalVar?: string) {
    this.y = 10;
    if (typeof jsonOrSomeVar=== "string") {
      super(jsonOrSomeVar);
    } else {
      super(someOtherVar, someAdditionalVar);
    }
  }
}

Note that the initialization order here is roughly the same as in other OOP languages and you need to be careful about not calling virtual methods from constructors, etc.

If that's too distasteful, note that the restriction is simply that the first statement be a super call. You can often refactor the super call:

class CcDerived extends CcDefinition {
  constructor (json: string);
  constructor (someVar: boolean, someOtherVar: number, someAdditionalVar: string);
  constructor (jsonOrSomeVar: any, someOtherVar?: number, someAdditionalVar?: string) {
      super(
          typeof jsonOrSomeVar === 'string' ? jsonOrSomeVar : someOtherVar,
          typeof jsonOrSomeVar === 'string' ? undefined : someAdditionalVar); 
  }
}

Not the prettiest, but it's at least semantically equivalent. This does assume your base class constructor is checking for undefined (instead of arguments.length) to determine which overload was invoked.

Lachrymatory answered 2/10, 2014 at 7:0 Comment(2)
Thanks Ryan, I had already found the 'ugly' version myself. I'll explore your first suggestion too, but my actual classes may be a bit too involved to actually use it.Patchwork
I can't get the first version to work with my code. I'll keep on at it - but it persists in reporting the original error. Even if both the base and derived classes initiliase their members in the constructor.Patchwork

© 2022 - 2024 — McMap. All rights reserved.