Search code examples
typescriptclassabstract

Why can't I access an abstract property in the constructor of a typescript class


abstract class Route {
    abstract readonly name?: string;
    protected abstract pattern: string;

    public constructor() {
        // Do something with `this.name` and `this.pattern`.
        
        console.log(this.pattern); // Typecheck error
    }
    
    abstract handle(): void;
}

This throws an error because this.pattern is not to be accessed in the constructor. Why can't I access it?


Solution

  • (Converting my comments to an answer)

    Why can't I access it?

    Because the derived class’ constructor won’t have been called, so the object may be in an invalid state. Some languages allow virtual-calls from a parent constructor but it’s still generally agreed to be a bad practice. TypeScript chose to disallow it.

    This is mentioned in the documentation: https://www.typescriptlang.org/docs/handbook/classes.html

    [...] each derived class that contains a constructor function must call super() which will execute the constructor of the base class. What’s more, before we ever access a property on this in a constructor body, we have to call super(). This is an important rule that TypeScript will enforce.

    The solution in in case is to pass pattern as a parameter to Route's constructor. If pattern cannot be determined by the subclass’ constructor prior to calling the parent constructor then you need to rethink your design.

    abstract class Route {
        
        constructor(
            private readonly pattern: string
        )
        {
            console.log( pattern );
        }
    }
    
    class Derived123 extends Route {
        
        constructor() {
            super( /*pattern:*/ "123" )
        }
    }
    
    class Derived456 extends Route {
        
        constructor() {
            super( /*pattern:*/ "456" )
        }
    }