1

When working with classes and subclasses, it's quite common to define a generic method in the base class, and use the instance specific variable inside it.

However, I can't figure out how to reach the correct static class variable inside methods from the base class.

Take for example the following code:

abstract class Unit<T extends Symbol> {
    public static factors: {};

    constructor(
        protected readonly type: T,
        protected value: number
    ) {}
}

class Energy extends Unit<typeof EnergySym> {

    public static factors = {
        J: 1,
        kJ: 1e3,
        MJ: 1e6,
        GJ: 1e9,
    };

    constructor(value: number, unit: keyof typeof Energy.factors = 'J') {
        super(EnergySym, value * Energy.factors[unit]);
    }

    get(unit: keyof typeof Energy.factors) {
        return this.value / Energy.factors[unit];
    }
}

It would require a lot less code when adding more types of Units, if we can put the get method inside the base class, by reaching into the static fields of the current class there.

In Python, you can for example use self.__class__.foo. Is there a JavaScript equivalent?

Additionally, is there a way to add the correct types for that?

b9s
  • 517
  • 4
  • 13
  • 1
    You're looking for `this.constructor`. (In the TypeScript type, just `this`). – Bergi May 13 '21 at 15:55
  • Btw, the *factor* is only determined by the prefix of the unit, and [those actually are the same for all SI units](https://en.wikipedia.org/wiki/Metric_prefix). – Bergi May 13 '21 at 15:56
  • `this.constructor` doesn't have any properties according to Typescript. Besides that, the question related to the typings remains unanswered. – b9s May 13 '21 at 16:00
  • 1
    In that case, see https://stackoverflow.com/questions/33387318/access-to-static-properties-via-this-constructor-in-typescript/, https://stackoverflow.com/questions/32057033/how-to-override-static-variables-from-inherited-derived-classes-in-typescript, https://stackoverflow.com/questions/29244119/how-to-access-static-members-from-instance-methods-in-typescript - TypeScript is lacking support for `this.constructor`, but it's still the proper way – Bergi May 13 '21 at 16:07

1 Answers1

0

I've managed to resolve it by adding an additional type parameter to the base class that is the union of all allowed units.

It's still required to define the allowed units for each subclass, which can be done with type FooUnits = keyof typeof Foo.units, assuming they're defined as a static field on the class itself.

One could still imagine that this could be done with even less code, but as far as I could tell there is no TS support for that.

abstract class Unit<T extends Symbol, U> {

    public value: number;

    constructor(
        protected readonly type: T,
        value: number,
        public unit: U
    ) {
        this.value = value * this.constructor['units'][unit]
    }

    get(unit: U): number {
        return this.value / this.constructor['units'][unit];
    }
}

type EnergyUnits = keyof typeof Energy.units;

export class Energy extends Unit<typeof EnergySym, EnergyUnits> {

    public static units = {
        J: 1,
        Wh: 3.6e3,
    };

    constructor(value: number, unit: EnergyUnits = 'J') {
        super(EnergySym, value, unit);
    }
}
b9s
  • 517
  • 4
  • 13