3

How I should modify generic type parameter U of areaCalculator argument of WithArea function for it to extend the base class of current MixinFactory instance?

type Constructor<T> = new (...args: any[]) => T;

interface IArea {
    readonly area: number;
}

class MixinFactory<T extends Constructor<Shape>> {
    constructor(public Superclass: T) {}

    WithArea(areaCalculator: <U extends Shape & IArea>(ctx: U) => number) {
        return new MixinFactory<T & Constructor<IArea>>(class extends this.Superclass implements IArea {
            get area(): number {
                return areaCalculator(this);
            }
        })
    }
}

class Shape {}

class RectRaw extends Shape {
    constructor(
        public x: number,
        public y: number,
        public w: number,
        public h: number) {
            super();
        }
}

const Rect = new MixinFactory(RectRaw).WithArea(ctx => {
    return ctx.w * ctx.h;
}).Superclass;

const rect = new Rect(10, 10, 20, 20);

console.log(rect.area);

Playground

1
  • 1
    It took me a while to understand your code, pretty complicated ) Commented Mar 21, 2021 at 14:53

1 Answer 1

3

Your areaCalculator function doesn't need to be generic; there is no need for an additional type parameter U, and this type parameter is not exposed by the method get area(): number.

The areaCalculator function only needs to be defined as accepting whatever object the constructor type T returns, so with the InstanceType helper, we can extract that and use it directly in the type annotation:

class MixinFactory<T extends Constructor<Shape>> {
    constructor(public Superclass: T) {}

    WithArea(areaCalculator: (ctx: InstanceType<T> & IArea) => number) {
        return new MixinFactory<T & Constructor<IArea>>(class extends this.Superclass implements IArea {
            get area(): number {
                return areaCalculator(this as InstanceType<T> & IArea);
            }
        })
    }
}

Note that a type assertion this as InstanceType<T> & IArea is needed because Typescript isn't able to figure out that if a class extends this.Superclass of type T then its instances must have type InstanceType<T>.

Playground Link

Sign up to request clarification or add additional context in comments.

3 Comments

This is correct. FYI there is a built-in utility type InstanceType<T> which is (I think) the same as your ConstructorReturnType<T>.
@LindaPaiste Thanks for pointing that out, I've improved the answer.
In your answer, ctx also has access to the whole inheritance chain, it's a nice bonus. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.