ThomasReggi
ThomasReggi

Reputation: 59345

Create abstract class that has methods returning instance this

I am trying to create an abstract class ParseContent that allows it's implementors to implement a parse method, the catch is that the ParseContent abstract class has other methods, that return instances of the inheriting class. is this possible?

export abstract class ParseContent extends RawContent { 
  parsed: any

  constructor(params: ConstructorParameters<typeof RawContent>[0] & {
    parsed: any
  }) {
    super(params)
    this.parsed = params.parsed
  }

  abstract parse<T> (content: string): T

  static fromString(opt: Parameters<typeof RawContent['fromString']>[0]) { 
    const pureOpts = super.fromStringOpts(opt)
    const parsed = this.parse(pureOpts.content)
    return new this({ ...pureOpts, parsed })
  }

  static async fromPath(opt: Parameters<typeof RawContent['fromPath']>[0]) { 
    const pureOpts = await super.fromPathOpts(opt)
    const parsed = this.parse(pureOpts.content)
    return new this({ ...pureOpts, parsed })
  }

}

export class JSONContent extends ParseContent { 
  static parse(content: string): any { 
    return JSON.parse(content)
  }
}

Upvotes: 1

Views: 179

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074148

Because you're using new this in your static members, they'll create instances of whatever class they're called on. For instance, calls to JSONContent.fromPath(...) (JSONContent inherits it from ParseContent, since ParseContent is the prototype of JSONContent — a feature fairly specific to JavaScript and TypeScript) will create a JSONContent instance, because during the call to fromPath, this is JSONContent. It will use the parse on JSONContent for the same reason: this is JSONContent.

Example:

class Parent {
    static wrapper() {
        return this.create();
    }
    static create() {
        const ctor = (this && this[Symbol.species]) || this;
        return new ctor();
    }
}

class Child extends Parent {
}
console.log(Child.create() instanceof Child);  // true
console.log(Child.wrapper() instanceof Child); // true

You can give subclasses the ability to override that with the species pattern (and probably others), but my sense of what you're asking is that you want the default behavior.

As for the TypeScript aspect of this, I'm afraid the news may not be good.

Upvotes: 1

Related Questions