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

"Block-scoped variable used before its declaration" not detected inside closures #9273

Closed
kourge opened this issue Jun 20, 2016 · 4 comments
Closed
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@kourge
Copy link

kourge commented Jun 20, 2016

This is similar to #7477 and #8976, but occurs inside closures.

TypeScript Version:

1.9.0-dev.20160620-1.0

Code

const y = (() => x)();
const x = 3;

Expected behavior:

The compilation error "Block-scoped variable x used before its declaration".

Actual behavior:

The code is compiled to:

var y = (function () { return x; })();
var x = 3;

This sets y to undefined.

Additional variants:

This also occurs when the access to the binding x is in default argument value position:

const y = ((i: number = x) => i)();
const x = 3;

Comparison against spec behavior:
All of the following result in a ReferenceError being thrown on Node 5.1:

(function() {
  'use strict';
  var y = (() => x)();
  const x = 3;
  return y;
})();

(function() {
  'use strict';
  const y = (() => x)();
  const x = 3;
  return y;
})();
@RyanCavanaugh RyanCavanaugh added Working as Intended The behavior described is the intended behavior; this is not a bug Suggestion An idea for TypeScript In Discussion Not yet reached consensus and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Jul 25, 2016
@RyanCavanaugh
Copy link
Member

Consider the dual of this, #9757, where someone didn't want an error even when the variable was initialized with a reference to itself.

We could special-case IIFEs to be considered inlined for the purposes of flow analysis, but in general we can't tell whether a given function expression executes "now" or "later", so that'd just be a patch, but it might be one worth making.

@kourge
Copy link
Author

kourge commented Jul 26, 2016

IIFEs are a very common pattern, and additionally they are distinguishable on a syntactic level. It would be a great boon for flow analysis to consider them in the special way, however limited this exception may be. But like you said, not being able to tell when a function expression gets invoked is a great limitation, and might even step into the realm of escape analysis.

@yortus
Copy link
Contributor

yortus commented Jul 27, 2016

IIFEs are a very common pattern [....] It would be a great boon for flow analysis to consider them in the special way

#8849 already implements this, so it's surprising that it doesn't catch use before declaration. Consider the same example but with a union type for x, and another identical IIFE just after the assignment:

const y = (() => x)(); // x is string|number, due to #8849 TS *knows* it hasn't been assigned
                       // when the IIFE runs (although unassigned consts can't exist!)

const x: string|number = 3;

const z = (() => x)(); // x is number, due to #8849 TS *knows* it must
                       // be a number when the IIFE runs

This shows that the compiler has treated the IIFEs as part of the surrounding control flow for the purposes of narrowing, but doesn't appear to recognise use before declaration of x.

@RyanCavanaugh
Copy link
Member

This is fixed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants