Repository navigation
Functions wrapped in __importDefault with allowSyntheticDefaultImports emit incorrect function calls #35420
Description
The following code:
import allSettled from "promise.allsettled";
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
allSettled([resolved, rejected]).then(results => {
console.log(results);
});Is transpiled into the following JS with esModuleInterop & allowSyntheticDefaultImports:
"use strict";
var __importDefault =
(this && this.__importDefault) ||
function(mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const promise_allsettled_1 = __importDefault(require("promise.allsettled"));
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
promise_allsettled_1.default([resolved, rejected]).then(results => {
console.log(results);
});Full tsconfig.json:
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
},
"include": [
"src"
]
}(CodeSandbox.)
Executing this code leads to:
TypeError: #<Object> is not a constructor
at Object.resolve (<anonymous>)
at Object.PromiseResolve (/sandbox/node_modules/es-abstract/es2018.js:160:10)
at /sandbox/node_modules/promise.allsettled/implementation.js:26:24
at Function.from (<anonymous>)
at Object.allSettled (/sandbox/node_modules/promise.allsettled/implementation.js:19:22)
at Object.allSettled [as default] (/sandbox/node_modules/promise.allsettled/index.js:16:9)
at Object.<anonymous> (/sandbox/src/demo.ts:6:11)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Module.m._compile (/sandbox/node_modules/ts-node/src/index.ts:536:23)
at Module._extensions..js (internal/modules/cjs/loader.js:787:10)
That is because the promise.allsettled package handles this according to the spec, see es-shims/Promise.allSettled#5. The author of that library is Jordan Harband (@ljharb) who will have deeper understanding of the specifics than I do. (Also, thanks for nudging me to report this issue!)
The problem is most likely the emitted function call, which looks like this:
promise_allsettled_1.default([resolved, rejected]).then(results => {
console.log(results);
});In comparison, Babel + TS emits this (which works):
(0, _promise.default)([resolved, rejected]).then(function (results) {
console.log(results);
});TypeScript Version: 3.7.2
Search Terms: esModuleInterop, allowSyntheticDefaultImports, emit, transpile, downlevel compile, ES Modules, default, wrapper, Node.js, module.
Activity
ljharb commentedon Nov 29, 2019
A simpler repro case would be importing
module.exports = function () { 'use strict'; return !!this; }(with esModuleInterop and synthetic imports enabled, of course) - a correct downleveling would make invoking this function with no implied receiver return false; TS’s output currently seems to make it return true.ajafff commentedon Dec 27, 2019
AFAICT the same bug exists for named imports.
Jordan Harband (@ljharb) please correct me if I'm wrong.
ljharb commentedon Dec 27, 2019
Yes, that’s the same bug, thanks.
fix receiver of imported and exported functions
ajafff commentedon Dec 27, 2019
Fix is up at #35877
fix receiver of imported and exported functions
fix receiver on calls of imported and exported functions (#35877)
gdh1995 commentedon Jul 4, 2021
Ron Buckton (@rbuckton) Sorry to disturb you, but your PR seems to wrap all imported symbols with
(0, ...), but I think it's not necessary and may be skipped when the target is a "const arrow function".Avoid marking any indirect function call as useless code.