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

Open
kourge opened this issue Jun 20, 2016 · 3 comments
Open

Comments

@kourge
Copy link

@kourge 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

This comment has been minimized.

Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Jul 25, 2016

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

This comment has been minimized.

Copy link
Author

@kourge 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

This comment has been minimized.

Copy link
Contributor

@yortus 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.