Skip to content

Replacing object reference before exporting causing problems #4101

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

Closed
tjoskar opened this issue Jul 31, 2015 · 8 comments
Closed

Replacing object reference before exporting causing problems #4101

tjoskar opened this issue Jul 31, 2015 · 8 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@tjoskar
Copy link

tjoskar commented Jul 31, 2015

Hi,

If I have the following typescript code:

var obj = {
    someKey: 6
};

// For some reason I want to change the object
obj = {
    someKey: 7
};

export {obj};

it compiles to:

var obj = {
    someKey: 6
};
exports.obj = obj;
obj = {
    someKey: 7
};

So if I importing the object I get:

import {obj} from './some-file';
console.log(obj.someKey === 6); // true

But I was expecting 7. I.e. I was expecting the following JS code:

var obj = {
    someKey: 6
};
obj = {
    someKey: 7
};
exports.obj = obj;

Is this a bug or expected behavior?

I am using version 1.6.0-dev.20150731 but it has the same behavior in 1.5.3.

@tjoskar
Copy link
Author

tjoskar commented Jul 31, 2015

A maybe more realistic example:

import {configInterface} from './config-interface';
import {defaultConfig} from './default';
import {localConfig} from './local';
import {productionConfig} from './production';
import {defaultsDeep} from 'lodash';

var config: configInterface;

if (process.env.NODE_ENV === 'production') {
    config = defaultsDeep<Object, configInterface>(productionConfig, defaultConfig);
} else {
    config = defaultsDeep<Object, configInterface>(localConfig, defaultConfig);
}

export {config};

If I import config after compiling, it will be undefined.
If I try to create the variable inside the if statement (which I don't want) I get the following code:

typescript:

if (true) {
    var k = {};
} else {
    var k = {};
}
export {k};

compiles to:

if (true) {
    var k = {};
}
else {
    var k = {};
}

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript High Priority labels Jul 31, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Jul 31, 2015

The current behavior is by design. we assume that the variable initialization happens at declaration site. We have talked about this before, and there is not a foolproof way of doing it. but i think a better approch is to emit the export declaration where it was in the input code relative to other statements, this way you at least have control over how it works.

@mhegazy mhegazy added this to the TypeScript 1.6 milestone Jul 31, 2015
@mhegazy mhegazy assigned yuit and sheetalkamat and unassigned yuit Jul 31, 2015
sheetalkamat added a commit that referenced this issue Aug 10, 2015
@ahejlsberg
Copy link
Member

As I mentioned in the comments on #4259, emitting the export assignment at the location of the export declaration will break the following code:

export { foo };
var foo = 1;

Export declarations will quite often appear before the entity they're exporting.

I don't know that there is a solution that doesn't have some problem, and I still think our current scheme is the best compromise. Another alternative is to put the assignment at the very end of the module, but emitting it at the place of the export declaration isn't a viable solution in my opinion.

@ahejlsberg
Copy link
Member

Just so we have all the data in one place, here are the possible solutions and their issues:

  • Emit export assignment at variable declaration site as we do now. Only works when declaration has an initializer.
  • Emit export assignment at export declaration site. Doesn't work when export declaration precedes variable declaration and/or assignments.
  • Emit export assignment at end of module. Doesn't work when module initialization code makes calls to functions in other modules and those functions access the exported variable (true for the first two above as well when function calls precede export assignment).
  • Rewrite variable references to export property references. Doesn't work when variable is exported by multiple names.
  • Rewrite to properties with get and set accessors. Gets us closer to ES6 semantics, but much larger change, doesn't perform as well, and doesn't work at all for ES3.

Probably the best alternative to our current scheme is to emit all the export assignments at the end of the module. But, really, we're just trading one set of issues for another.

@mhegazy
Copy link
Contributor

mhegazy commented Aug 13, 2015

@ahejlsberg if we can not, or would not, emulate the ES6 semantics correctly by rewriting references, emitting the export declaration in the place it is authored is easier for users to understand.

I would argue that the case reported in this issue is more common than an export declaration before the variable is assigned. also see related report in #4162.

Moreover, with allowing the export declaration to be emitted at the user specified location, the user has a chance to workaround it, by moving the export declaration to the end of the file, where as the current scheme, there is no way to solve it unless you declare a new variable, and export that instead, which is not intuitive.

@ahejlsberg
Copy link
Member

Yes, emitting the export assignment at the location of the export declaration allows the user to work around the problem. But it doesn't really solve the problem. If we're going to change this, wouldn't we solve more problems by emitting the export assignments at the end of the module? Surely this would make even more real world code work correctly. I think there are far more cases of export declarations preceding the variable declaration (which would become broken) than there are cases of the module initialization code making calls to functions in other modules that access exported variables of the calling module.

@cspotcode
Copy link

  • Rewrite variable references to export property references. Doesn't work when variable is exported by multiple names.

Can you rewrite all variable assignments to also assign to the export? No need for getters. The local variable and all exports are kept in sync every time the exported variable is assigned.

export {foo as one, foo as two}
var foo = "hello";
foo = "world";

...generates:

var foo = exports.one = exports.two = "hello";
foo = exports.one = exports.two = "world";

Sorry to bump an old issue.

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Sep 14, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Sep 14, 2016

This should be working as intended in latest.

@mhegazy mhegazy closed this as completed Sep 14, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

7 participants