-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Optional chaining with default parameter fails #36295
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
Comments
This looks very similar to #29159. Note that this is only an error in strict mode. In sloppy mode it will run just fine, but reference the wrong variable. |
Ah, good find. I didn’t see that when I searched. It’s true that it’s only a problem in strict mode, but that’s the vast majority of code I write because I’m using ES modules. Also only a problem when targeting >ES5, but again that’s almost everything I write because certain features don’t work well otherwise, such as for-of. Not true for everyone else? |
Also just ran into this. Here's an alternative repro if it helps. In discussing this with some coworkers, they brought up this note from this section of the spec:
It appears that is not being followed in the current implementation. |
In particular, it depends on whether the code is in strict mode or not. |
There are two ways to address this:
|
@DanielRosenwasser any preference? |
I think the second as long as it's not too much work. By the way, there's also #2584 |
Some clarification, #29159 is actually working as intended as it is illegal for the initializer of one parameter to reference another parameter that follows it. However, the following is still incorrect: // ts
// @target: ES6
((b = class { static x = 1 }) => {})();
// js
"use strict";
((b = (_a = class {
},
_a.x = 1,
_a)) => { var _a; })(); |
@DanielRosenwasser I have a fix that merely moves the initializer to the body, but that might mean that we need to issue an error when there are shadowed identifiers in the body that were references in the initializer: function a() {}
function b(x = a()) {
let a;
} We issue an error for example above when targeting ES5 but not when targeting ES2015 or later (as the // ts
// @target: ES2015
function a() {}
function b(x = a()?.b) {
let a;
}
// js
function a() {}
function b(x) {
if (x === void 0) { x = (_a = a()) === null || _a === void 0 ? void 0 : _a.d; }
let a;
} This will move the definition to the body, just as we do in the ES5 emit and run into the scoping issue. |
Reporting the correct error is not impossible but would be significantly more difficult than using an IIFE and preserving the scope: // js
function a() {}
function b(x = (() => { var a; return (_a = a()) === null || _a === void 0 ? void 0 : _a.d; })()) {
let a;
} With the IIFE there's no need to move the initializer so the scope is preserved. |
TypeScript Version: 3.7.2 & Nightly (2020-01-18)
Search Terms: optional chaining, default parameters,
_a is not defined
Code
Expected behavior:
This should run without error.
Actual behavior:
This throws an error:
ReferenceError: _a is not defined
Playground Link: link
Related Issues: n/a
Here's the code it compiles to now for illustration:
This error happens because
_a
is used before it's defined. TypeScript adds avar _a;
inside the function body, but because the assignment occurs in the parameter_a
is not in scope and the assignment fails. I'm not sure what the preferred solution would be here. I solved it in decaffeinate by using a helper function, though I never really liked that solution.The text was updated successfully, but these errors were encountered: