Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

TypeScript keeps transpiling away async/await even with target: es2017

Consider this test script.

#!/bin/bash -ex

rm -rf ts-async-await
mkdir ts-async-await
cd ts-async-await
npm init -y &> /dev/null
npm i --save-dev typescript@4.7.4 &> /dev/null
echo "async function x() { return 1; }" > test.ts
echo '{ "compilerOptions": { "target": "es2017" } }' > tsconfig.json
npx tsc --version
npx tsc --showConfig
npx tsc test.ts
cat tsconfig.json
cat test.js

I’m creating an empty directory with a single one-liner TypeScript file, containing async/await.

async function x() { return 1; }

Then, I’m creating a tsconfig.json where the compilerOptions target is set to es2017.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

{ "compilerOptions": { "target": "es2017" } }

tsc --showConfig shows:

{
    "compilerOptions": {
        "target": "es2017"
    },
    "files": [
        "./test.ts"
    ]
}

Then, I’m transpiling my test.ts file to test.js.

Expected: This one-liner should transpile cleanly to a one-liner JS file, just like it does in the TypeScript playground https://www.typescriptlang.org/play?#code/IYZwngdgxgBAZgV2gFwJYHsIwB4AoCUMA3jAE4CmyCpWAjANwwC+QA

Actual: TypeScript generates 40 lines of code, downgrading my async/await code into non-async/await code with the __awaiter and __generator helpers.

How do I make my sample TypeScript project do what the playground does? Why isn’t target doing the right thing for me here?

Here’s the full log of the test output:

+ rm -rf ts-async-await
+ mkdir ts-async-await
+ cd ts-async-await
+ npm init -y
+ npm i --save-dev typescript@4.7.4
+ echo 'async function x() { return 1; }'
+ echo '{ "compilerOptions": { "target": "es2017" } }'
+ npx tsc --version
Version 4.7.4
+ npx tsc --showConfig
{
    "compilerOptions": {
        "target": "es2017"
    },
    "files": [
        "./test.ts"
    ]
}
+ npx tsc test.ts
+ cat tsconfig.json
{ "compilerOptions": { "target": "es2017" } }
+ cat test.js
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
function x() {
    return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
        return [2 /*return*/, 1];
    }); });
}

>Solution :

Typescript either runs as a CLI invocation on a file, or on a config that specifies the entry point, but not both (try running npx tsc test.js --p tsconfig.js and look at the output you get).

As such, this will work just fine:

> npx tsc test.ts --target esnext

and this will also work just fine:

> npx tsc

or with explicit reference to the config file:

> npx tsc --p tsconfig.json

But because you’re calling npx tsc test.ts in your script, typescript is going to completely ignore your config file and just run what you told it to do purely in your CLI statement.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading