Skip to content
Advertisement

Creating uninherited/unoverridable/nonvirtual methods in TypeScript

I’m super stumped. I want to create a family of objects that all share common properties, but don’t share methods. I would like

class X {
  x: any;
  myFunc() {
    console.log(this.x);
  }

  constructor() {
    this.x = true;
    this.myFunc();
  }
}

class Y extends X {
  y: any;
  myFunc() {
    console.log(this.y);
  }

  constructor() {
    super();
    this.y = 2;
    this.myFunc();
  }
}

class Z extends Y {
  z: any;
  myFunc() {
    console.log(this.z);
  }

  constructor() {
    super();
    this.z = "something stupid";
    this.myFunc();
  }
}

new Z(); 
// should print
// true
// 2 
// something stupid
new Y();
// should print
// true
// 2
new X();
// should print 
// true

The current behavior is for new Z() to print something stupid 3 times.

I would like a way to call a method inside a class that isn’t overridden, but calls outside are. In C++, you can define a virtual public method and a private non-virtual method, and I would like to replicate something like that.

Advertisement

Answer

Update in response to your comments and new question information:

It seems like you want a combination of override (public) methods and private (method) fields:

TS Playground

class X {
  x: any;
  #fn() { console.log(this.x); }
  fn() { this.#fn(); }

  constructor() {
    this.x = true;
    this.#fn();
  }
}

class Y extends X {
  y: any;
  #fn() { console.log(this.y); }
  override fn() { this.#fn(); }

  constructor() {
    super();
    this.y = 2;
    this.#fn();
  }
}

class Z extends Y {
  z: any;
  #fn() { console.log(this.z); }
  override fn() { this.#fn(); }

  constructor() {
    super();
    this.z = "something stupid";
    this.#fn();
  }
}

const z = new Z(); // true, 2, "something stupid"
z.fn(); // "something stupid"

const y = new Y(); // true, 2
y.fn(); // 2

const x = new X(); // true
x.fn(); // true


Original answer:

You can do it by extending each of them from the same abstract class rather extending each from the previous. If you’re extending each from the previous for the purpose of instantiation side-effects, then extension is probably not the optimal pattern, but I can’t make another suggestion without a more concrete explanation of the problem:

TS Playground

abstract class C {
  abstract myFunc (): void;
}

class X extends C {
  x: any;

  myFunc() {
    console.log("x");
  }

  constructor() {
    super();
    this.x = true;
    this.myFunc();
  }
}

class Y extends C {
  y: any;

  myFunc() {
    console.log("y");
  }

  constructor() {
    super();
    this.y = 2;
    this.myFunc();
  }
}

class Z extends C {
  z: any;

  myFunc() {
    console.log("z");
  }

  constructor() {
    super();
    this.z = "something stupid";
    this.myFunc();
  }
}

new Z(); // logs "z"


Note that, other than the nominal inheritance of the abstract class, this pattern is no different than implementing from an interface:

TS Playground

interface C {
  myFunc (): void;
}

class X implements C {
  x: any;

  myFunc() {
    console.log("x");
  }

  constructor() {
    this.x = true;
    this.myFunc();
  }
}

class Y implements C {
  y: any;

  myFunc() {
    console.log("y");
  }

  constructor() {
    this.y = 2;
    this.myFunc();
  }
}

class Z implements C {
  z: any;

  myFunc() {
    console.log("z");
  }

  constructor() {
    this.z = "something stupid";
    this.myFunc();
  }
}

new Z(); // logs "z"

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement