Alexis Coles
Alexis Coles

Reputation: 1317

Strange Typescript compiler Error: 'this' cannot be referenced in current location, Can anyone explain please?

I presume there is a good reason for this but it seems very strange to me. Please take the following code as an example of the issue.

class Foo {
    constructor(referenceMethod: () => void) {
    }
}

class Bar extends Foo {

    TestProperty: string = "boo";

    constructor() {
        super(this.ReferenceMethod);
    }

    ReferenceMethod() {

    }
}

This produces the compiler error

'this' cannot be referenced in current location.

If we initialise the TestProperty inside the constructor the compiler is happy and the world spins round as expected and of course if we don't pass the reference to the referenceMethod to the super call we can set the TestProperty outside the constructor fine.

However I just don't understand why doing both these things together causes a problem. If anyone could shed some light on this to help my understanding would be much appreciated.

Upvotes: 6

Views: 9511

Answers (3)

Rafael
Rafael

Reputation: 2753

If someone steps with this error, also happens when using the private or public modifiers inside a constructor.

Try:

constructor(x : number) {
    ... super call with this reference
    this.x = x;
}

private x : number;

Instead of:

constructor(private x: number) {
     ...super calls, compiler error here
}

Upvotes: 0

Samuel Neff
Samuel Neff

Reputation: 74929

It seems like Foo and Bar have different behaviors. Foo accepts a callback as its constructor but Bar has the callback as an instance method. What is your real-world use case? Perhaps Bar should compose (contain) Foo and not extend it.

Alternatively you can modify the behavior of Foo to accept an optional callback and also define an overridable instance method that is used as default. Then the derived class can ignore the callback argument and just override the instance method instead.

Option 1: Composition

class Foo {
    constructor(referenceMethod: () => void) {
    }
}

class Bar {

    TestProperty: string = "boo";
    foo: Foo;

    constructor() {
        this.foo = new Foo(this.ReferenceMethod);
    }

    ReferenceMethod() {

    }
}

Option 2: Reference method as optional arg and overridable

class Foo {
    constructor(referenceMethod?: () => void) {
        if (referenceMethod) {
            this.ReferenceMethod = referenceMethod;
        }
    }

    ReferenceMethod():void {}
}

class Bar {

    TestProperty: string = "boo";

    constructor() {
    }

    ReferenceMethod():void {

    }
}

Upvotes: 1

WiredPrairie
WiredPrairie

Reputation: 59773

From 8.3.2 of the Typescript language specification:

The first statement in the body of a constructor must be a super call if both of the following are true: 1. The containing class is a derived class.
2. The constructor declares parameter properties or the containing class declares instance member variables with initializers.

The containing class in your example declares a variable called TestProperty that uses an initializer:

 TestProperty: string = "boo";

The reason for the error you're seeing is that the compiler requires the first line of the constructor to be the call to the super given the current layout of your class (#2 from the language specification above). Because of that, it means that the class isn't fully initialized when you start to reference properties on the class, like TestProperty. While ReferenceMethod would be initialized and available, the member variables still would not be, which could break other functionality in your class.

While the language authors could have generated working code that avoids this issue, it's clearly easier and more consistent behavior when it works the way you've found.

As an alternative, you might try adding an initialize method that performs the same logic once the constructor has initialized the class fully. It's admittedly a second step, but, it works well with TypeScript. But, be careful to make sure that initialize is called after the constructor has initialized member variables.

Upvotes: 6

Related Questions