-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Different result running native vs down-levelled async function #6068
Comments
@bterlson should Edge have errored on assigning to Promise, on executing the asynchronous function, or is it exhibiting correct behavior? |
@DanielRosenwasser this seems to have been asked on tc39/ecmascript-asyncawait here, and answered in the comment thread for that issue by @domenic and @bterlson here and here. That issue talks about replacing/modifying If changing |
Ideally transpilers should do something like this in a "header" file that runs before any author code: const $$typeScriptRuntime = {
Promise: global.Promise,
PromiseResolve: global.Promise.resolve,
PromiseThen: global.Promise.prototype.then
}; then when they actually use it, instead of just straight transpiling to (Also the TypeScript transpiler's "cast" function seems entirely unnecessary; just use |
When @rbuckton gets back he should probably be in this discussion. |
@domenic Unfortunately getting the @yortus I don't think we will go to exhaustive lengths to resolve the native "Promise". We would have a similar issue for "Math" when using the transpiled Did you get any kind of error when you redefined |
@yortus This doesn't fail because we do not report semantic errors for JavaScript files. If you rename "original.js" to "original.ts" and run the sample, you get the following error:
I do see an issue that we do not report an error if "original.ts" is a module for an async function with an inferred return type. I will look into providing a better error for this. |
@rbuckton understandable re // file: test.ts
import * as Promise from 'bluebird';
var foo = async () => (await Promise.delay(500), 42);
foo()
.then(value => console.log(`Result: ${value}`))
.catch(error => console.log(`Error: ${error}`))
.finally(() => console.log('All done!'));
// OUTPUT of "node ./test.js" after running "tsc --target es6 --module commonjs"):
// Result: 42
// All done!
// OUTPUT of running test.ts code directly in MS Edge (no tsc step):
// SCRIPT438: Object doesn't support property or method 'finally' It's not hard to see this happening in real code. People are starting to use ES7 async functions. And things like Yet in this situation TSC produces code with different semantics, currently without any compiler warnings. The code may behave differently in unexpected ways depending how/whether it's transpiled. And we are talking about a situation with well-defined ES semantics (unless there are significant changes to https://tc39.github.io/ecmascript-asyncawait/ between stage 3 and 4). |
The upshot of my previous comment is that the code in ** unless significant changes to https://tc39.github.io/ecmascript-asyncawait/ between stage 3 and 4 |
We have the same issue if you use // original.js
var Object = "What could possibly go wrong?";
export class C {
get X { return 1; }
}
// downlevel.js
var Object = "What could possibly go wrong?";
var C = (function () {
function C() {
}
Object.defineProperty(C.prototype, "X", { // throws
get: function() { return 1; }
enumerable: true,
configurable: true
});
return C;
})();
exports.C = C; |
Sure, but that's not the issue in |
@yortus We cannot always guarantee 100% support for ES7 semantics when transpiling down to ES6/ES5. If you run your What we can do is do the same thing we do for import * as Promise from "bluebird";
export var foo = async () => Promise.delay(500); Would report the error:
As a result, you would not be able to create a local binding for import * as bluebird from "bluebird";
export foo = async () => bluebird.delay(500); // type: Promise<void> |
I think that would be a good policy given how downlevelling is done. It will catch at compile time what could otherwise be confusing runtime errors.
How about the following idea? Instead of reserving A quick google suggests getting the global object is not so hard as it seems, eg: function __getGlobalObject() {
try {
// Get the global object in ES3/5/6/7, in either strict and non-strict mode
return Function('return this')();
}
catch(e) {
// Must be running in browser with Content Security Policy headers set
return window;
}
} This helper might in fact be useful for transpiling any file that defines local aliases of global objects that are needed by other helpers, such as |
As I understand it, using var __global = typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : undefined; That still has issues with anyone locally defining "global" or "self" or "window", which puts us back in the same boat. |
I've seen countless files with a locally defined Taking the OTOH reserving |
Pretty sure |
Yes, it works as long as you are not running inside a sandboxed host like Caja/Secure ECMAScript (SES). |
If that's a small fraction of the TypeScript user base, which I imagine it is, they could provide their own It would be good to favour the common case IMHO. I'd prefer to continue allowing code with EDIT: maybe a helper |
Has nobody mentioned the obvious solution that instead of reserving |
@domenic I am considering that approach as well as several others. Another direction is to rename locally defined variables or imports named |
Is caja even alive? I've been trying to mess around with it but both playgrounds seem to have link-rotted. The main playground says 'Updated Dec 22, 2009'. |
@DanielRosenwasser actually it was this: const globalObject: any = Function('return this')();
export default globalObject; |
@kitsonk Yup, sorry about that. Thanks for the correction. |
@kitsonk @DanielRosenwasser That's why I suggested adding a new helper like |
Out of curiosity, what would you replace it with in those environments? Is there any options when running under the strict prolog then? |
@kitsonk probably I wrote in an earlier comment one possible default implementation that would cover most cases: function __getGlobalObject() {
try {
// Get the global object in ES3/5/6/7, in either strict and non-strict mode
return Function('return this')();
}
catch(e) {
// Must be running in browser with Content Security Policy headers set
return window;
}
} |
I spoke with @mhegazy and for now we will issue an error per this comment. I should have a PR for this shortly, once a prerequisite PR has been merged. |
Reserving @rbuckton can you clarify if the PR you mentioned is being considered as a stopgap solution, or more final? |
Quick search on github for TypeScript code containing Obviously only some fraction of that will introduce async/await and hit this new error, but at least it shows it's fairly widespread practice to use a local |
It will cause a lot of breakage in Dojo 2 I suspect, as we shim it without touching the global namespace, because we can't be sure we aren't running with other code that doesn't pollute it. Even if we don't use async/await, anyone wanting to use it will end up likely breaking their code and may not be totally clear on why. |
Agreed. The error message they will see is:
When in fact Also it may be unclear for experienced JS devs why TS rejects this code, since it always worked before and is valid ES6. |
@yortus The message is consistent with the error we report for using |
This is fixed as of #6631. Please refer to the following comments as to the reasons and rationale for this change:
We will be updating our Breaking Changes documentation for TypeScript 1.8 to inform users as to the change and its implications. |
In the following example, running
original.js
directly in MS Edge 25 / EdgeHTML 13 (with experimental JavaScript enabled) outputs"Get to the chopper!"
to the console.downlevel.js
is produced by runningtypescript@next
(I used v1.8.0-dev.20151210) overoriginal.js
with the--allowJs
option. Runningdownlevel.js
in MS Edge outputs an errorSCRIPT445: Object doesn't support this action
.It looks like the down-levelled code picks up the locally-scoped
Promise
instead of using the nativePromise
.The text was updated successfully, but these errors were encountered: