DEV Community

Thomas Reggi
Thomas Reggi

Posted on

Inheritable Static Class Methods and Overriding

I like Object oriented programming (oop), and inheritance, but it can be painful at times when you can't express yourself fully.

Typing things in typescript can be tricky.

I often like to use static class initializers (useful for async operations), but I've been struggling with how to inherit initializers. I figured it out and thought I'd put it somewhere for safe keeping.

class Foo {
    constructor (public props?: {
        alpha?: boolean
    }) {}
    static init <T extends typeof Foo>(this: T, props?: ConstructorParameters<T>[0]): InstanceType<T>  {
        return new this(props) as InstanceType<T>;
    }
}

class Bar extends Foo {
    constructor (public props?: NonNullable<ConstructorParameters<typeof Foo>[0]> & {
        beta?: boolean
    }) {
        super();
    }
    static init <T extends typeof Bar>(this: T, props?: ConstructorParameters<T>[0]): InstanceType<T>  {
        return new this(props) as InstanceType<T>;
    }
}

const x = Foo.init({ alpha: true })
const y = Bar.init({ alpha: true, beta: true })
Enter fullscreen mode Exit fullscreen mode

Playground

Here you can see that we have Foo and Bar they both have their own init method, that returns their respective class instances.

We can further expand on this.

Taking this another step further, we may want to extend the parent's init method, and run it was well. This allows us to chain the encapsulated / replaced functionality through to the children classes.

class Foo {
    constructor (public props?: {
        alpha?: boolean
    }) {}
    dogs?: boolean;
    init () {
        this.dogs = true;
        return this;
    }
    static init <T extends typeof Foo>(this: T, props?: ConstructorParameters<T>[0]): InstanceType<T>  {
        const t = new this(props) as InstanceType<T>;
        this.init();
        return t;
    }
}

class Bar extends Foo {
    constructor (public props?: NonNullable<ConstructorParameters<typeof Foo>[0]> & {
        beta?: boolean
    }) {
        super();
    }
    cats?: boolean;
    init () {
        super.init();
        this.cats = true;
        return this;
    }
    static init <T extends typeof Bar>(this: T, props?: ConstructorParameters<T>[0]): InstanceType<T>  {
        const t = new this(props) as InstanceType<T>;
        t.init();
        return t;
    }
}

const y = Bar.init({ alpha: true, beta: true })
console.log(y.dogs);
Enter fullscreen mode Exit fullscreen mode

Playground

This is one trick we can add to the typescript oop toolbox, to fully express ourselves better.

Top comments (0)