X Tutup
The Wayback Machine - https://web.archive.org/web/20210105102700/https://github.com/microsoft/TypeScript/pull/29374
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowed non-this, non-super code before super call in derived classes with property initializers #29374

Open
wants to merge 38 commits into
base: master
from

Conversation

@JoshuaKGoldberg
Copy link
Contributor

@JoshuaKGoldberg JoshuaKGoldberg commented Jan 11, 2019

Starts on #8277 by allowing the non-this, non-super code to be root-level statements in the constructor. This will now be allowed:

class Base { }
class Derived extends Base {
    public prop = true;
    constructor(public paramProp = true) {
        console.log("Hello, world!");
        super();
    }
}

It feels wrong to put a new forEachChild loop in the checker, though in the vast majority of user files this will be a very quick one. Is there a better way to check for a reference to super or this?

Edit 2/28: I've mostly resolved the merge conflicts introduced by both # private fields & useDefineForClassFields, but I'm not confident my approach is still a valid one. I'd greatly appreciate it if someone could confirm I'm on the right track!
Oh, and gulp runtests passes locally (on Windows). I'll try a Mac to see if there's some odd encoding/whitespace behavior with the failing test... ✔️

Fixes #8277.

It feels wrong to put a new `forEachChild` loop in the checker, though in the vast majority of user files this will be a very quick one. Is there a better way to check for a reference to `super` or `this`?
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/utilities.ts Outdated Show resolved Hide resolved
src/compiler/utilities.ts Outdated Show resolved Hide resolved
@weswigham
Copy link
Member

@weswigham weswigham commented Jan 11, 2019

It feels wrong to put a new forEachChild loop in the checker, though in the vast majority of user files this will be a very quick one. Is there a better way to check for a reference to super or this?

TransformFlags.Super | TransformFlags.ContainsSuper for super at least.

src/compiler/utilities.ts Outdated Show resolved Hide resolved
…boundaries

```ts
function () {
    return this;
}
```

It was immediately going to `ts.forEachChild` so the statement itself wasn't being counted as a new `this` scope.
Copy link
Contributor

@ajafff ajafff left a comment

This is getting very complex very fast. I wonder if there's an easier way using the control flow graph?

src/compiler/utilities.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/utilities.ts Outdated Show resolved Hide resolved
src/compiler/utilities.ts Outdated Show resolved Hide resolved
src/compiler/utilities.ts Outdated Show resolved Hide resolved
As per discussion in the issue, it would be ideal to consider any block that always ends up calling to super() the equivalent of a root-level super() statement. This would be valid:

```ts
foo = 1;
constructor() {
    condition() ? super(1) : super(0);
    this.foo;
}
```

...as it would compile to the equivalent of:
```ts
function () {
    condition() ? super(1) : super(0);
    this.foo = 1;
    this.foo;
}

That change would a bit more intense and I'm very timid, so leaving it out of this PR. In the meantime the requirement is that the super() statement must itself be root-level.
src/compiler/utilities.ts Outdated Show resolved Hide resolved
@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Jan 25, 2019

cc @ahejlsberg for review

@JoshuaKGoldberg
Copy link
Contributor Author

@JoshuaKGoldberg JoshuaKGoldberg commented Apr 2, 2019

Ping @ahejlsberg - is there anything that needs to be done here? It'd be nice to have this in 😄

@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.6.0 milestone Jul 12, 2019
@JoshuaKGoldberg
Copy link
Contributor Author

@JoshuaKGoldberg JoshuaKGoldberg commented Jul 16, 2019

Correction: ping, @weswigham?

@JoshuaKGoldberg JoshuaKGoldberg requested a review from weswigham Mar 5, 2020
@JoshuaKGoldberg
Copy link
Contributor Author

@JoshuaKGoldberg JoshuaKGoldberg commented Mar 5, 2020

@weswigham I believe I've addressed all the discussions on this PR, and it should be ready for re-review when you have a chance please!

src/compiler/utilities.ts Outdated Show resolved Hide resolved
Copy link
Member

@weswigham weswigham left a comment

This looks OK to me, but @rbuckton should have a look before it's merged~

@weswigham weswigham assigned rbuckton and unassigned weswigham Mar 5, 2020
@JoshuaKGoldberg
Copy link
Contributor Author

@JoshuaKGoldberg JoshuaKGoldberg commented May 14, 2020

Ping @rbuckton - now that 3.9 is out, I think it'd be lovely to get this into 4.0. Do you know if you'll be able to review this? (please 🙏 )

@DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented May 27, 2020

Was chatting with @ahejlsberg about this about 2 weeks ago - I recall agreeing that it'd be useful for 4.0, short of maybe some style nits. As a related change, he was also looking into ]our this-before-super() checks being too rudimentary. Maybe he can give an update on the whole picture.

btw, one thing I'd recommend is reviewing with whitespace changes off (https://github.com/microsoft/TypeScript/pull/29374/files?w=1)

prop = 1;
constructor() {
Math.random()
? super(1)

This comment has been minimized.

@rbuckton

rbuckton Aug 28, 2020
Member

Perhaps for a future PR, but it would be nice to actually support this. Its technically possible:

// ts
class Derived extends Base {
  prop = 1;
  constructor() {
    Math.random() ? super(1) : super(0);
  }
}
// es2015
class Derived extends Base {
  constructor() {
    // wrap `super` and initialization in an arrow
    const _super = (...args) => {
      super(...args);
      this.prop = 1;
      return this;
    };

    // class body
    Math.random() ? _super(1) : _super(0);
  }
}
// es5
var Derived = /** @class */ (function (_super) {
  __extends(Derived, _super);
  function Derived() {
    var _this = this;
    // wrap `super` and initialization in a function expression
    var _super_1 = function() {
      _this = _super.apply(_this, arguments) || _this;
      _this.prop = 1;
      return _this;
    };

    // class body
    Math.random() ? _super_1(1) : _super_1(0);
  }
  return Derived;
}(Base));
src/compiler/utilities.ts Outdated Show resolved Hide resolved
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// Object properties can have computed names; only method-like bodies start a new scope

This comment has been minimized.

@rbuckton

rbuckton Dec 1, 2020
Member

Do we need to be concerned about parameter initializers?

class C extends Base {
    constructor(public x: number, y = () => this.x) {
      super();
    }
}

This comment has been minimized.

@JoshuaKGoldberg

JoshuaKGoldberg Dec 2, 2020
Author Contributor

I don't believe we do? In the above, the () => this.x is the new scope, which should be covered by SyntaxKind.ArrowFunction.

src/compiler/transformers/ts.ts Outdated Show resolved Hide resolved
src/compiler/transformers/ts.ts Outdated Show resolved Hide resolved
src/compiler/transformers/es2015.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
PR Backlog automation moved this from Needs review to Waiting on author Dec 1, 2020
@typescript-bot
Copy link
Collaborator

@typescript-bot typescript-bot commented Dec 2, 2020

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

@JoshuaKGoldberg JoshuaKGoldberg requested a review from rbuckton Dec 2, 2020
@JoshuaKGoldberg
Copy link
Contributor Author

@JoshuaKGoldberg JoshuaKGoldberg commented Dec 9, 2020

Ping @rbuckton - is there anything else you'd like changed?

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

cc @DanielRosenwasser; I'm hesitant to mark this as fixing the linked issue, as it only partially does that. Edge case in the tooling maybe?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
PR Backlog
  
Waiting on author
Linked issues

Successfully merging this pull request may close these issues.

None yet

9 participants
You can’t perform that action at this time.
X Tutup