Ar3s
Ar3s

Reputation: 2307

Forward-closure of `this` before calling `super(...)` (or: "the mind-bending story of WTF seriously")

after some fiddling, I ended up writing this in TypeScript:

 class B extends A{
     constructor() {
         const c = new C(() => this.method());
         super(c);
         c.someMethod(this);
     }
 }

And it works ...

Let's set aside why I did this for a second (irrelevant to my question)

My questions are :

I am missing something there :/

Upvotes: 0

Views: 30

Answers (1)

jcalz
jcalz

Reputation: 327964

Yeah, I think I see why this is bothering you. The relevant issue in GitHub is Microsoft/TypeScript#3311. Let's see if I can answer these questions:


  • How in the world does it compile when I do things before the call to super ?!

Apparently it's because you don't have member initializers. From the linked issue:

You can call super whenever you want if you don't have member initializers...

If you look at how class member initializers are emitted, the reasoning for this becomes fairly clear. Member initializers are emitted after the super call (otherwise your derived-class initializers wouldn't properly overwrite base class initializations); by enforcing the super call to be the first thing the compiler can guarantee that there's exactly one (correct) place to emit those initializers.

You move the super call to any position by removing member initializers -- this makes explicit what you expect the initialization order of each class member to be.

I think the language designers think there are some legitimate things someone could do before the super call (e.g., doing some computations to determine what to pass into the call to super), but that a few things are problematic: member initializers (which emit to JS as references to this), and explicit references to this.


  • at least this should not be available or I'd rather have done const c = new C(this.method); but it did not compile, complaining that this is not, yet, available

Right, you can't refer to the bare this.method before the call to super because this is not guaranteed to exist at that time.


  • How can the line const c = new C(() => this.method()); closure this when it is not yet defined ?

My guess about the closure being accepted is that the reference to this must not be considered to come lexically before the super call. This might or might not be a bug or design limitation in the compiler. In your case, if the constructor of C doesn't actually call the function, then there's probably not an issue... the closure can potentially defer the dereferencing of this.method until after the subclass constructor is complete. But since the constructor of C might call the function, the code could be unsafe.

If you feel strongly that this should give a compiler warning, you might want to raise an issue in GitHub and reference Microsoft/TypeScript#3311 (you can't comment on that issue because it's been locked).

Hope that helps. Good luck!

Upvotes: 2

Related Questions