TypeScript编译ES6目标后Async/Await未正常保留问题求助
为什么TypeScript编译后async/await被转成了模拟代码?
我是TypeScript新手,写了一段调用psn-api获取PS用户奖杯数据的代码,用tsc index.ts编译后生成的JS里,async/await没有保留原生语法,反而变成了一堆带__awaiter、__generator的代码。我的tsconfig已经设置了target为ES6、module为CommonJS,环境是Windows11、VSCode,Node.js是官网安装的最新版,TypeScript通过npm安装。
我的TypeScript代码
import * as fs from "fs"; import type { Trophy } from "psn-api"; import { exchangeCodeForAccessToken, exchangeNpssoForCode, getTitleTrophies, getUserTitles, getUserTrophiesEarnedForTitle, makeUniversalSearch, TrophyRarity } from "psn-api"; async function main() { // 1. Authenticate and become authorized with PSN. // See the Authenticating Manually docs for how to get your NPSSO. const accessCode = await exchangeNpssoForCode("GqO4rje8Hx1sFqeEbzckLYND101S51LqWaXIrlMbC7TWybbZAYZygCqCSE8wx64E"); const authorization = await exchangeCodeForAccessToken(accessCode); // 2. Get the user's `accountId` from the username. const allAccountsSearchResults = await makeUniversalSearch( authorization, "andykasen13", "SocialAllAccounts" ); const targetAccountId = allAccountsSearchResults.domainResponses[0].results[0].socialMetadata .accountId; // 3. Get the user's list of titles (games). const { trophyTitles } = await getUserTitles(authorization, targetAccountId); const games: any[] = []; for (const title of trophyTitles) { // 4. Get the list of trophies for each of the user's titles. const { trophies: titleTrophies } = await getTitleTrophies( authorization, title.npCommunicationId, "all", { npServiceName: title.trophyTitlePlatform !== "PS5" ? "trophy" : undefined } ); // 5. Get the list of _earned_ trophies for each of the user's titles. const { trophies: earnedTrophies } = await getUserTrophiesEarnedForTitle( authorization, targetAccountId, title.npCommunicationId, "all", { npServiceName: title.trophyTitlePlatform !== "PS5" ? "trophy" : undefined } ); // 6. Merge the two trophy lists. const mergedTrophies = mergeTrophyLists(titleTrophies, earnedTrophies); games.push({ gameName: title.trophyTitleName, platform: title.trophyTitlePlatform, trophyTypeCounts: title.definedTrophies, earnedCounts: title.earnedTrophies, trophyList: mergedTrophies }); } // 7. Write to a JSON file. fs.writeFileSync("./games.json", JSON.stringify(games)); } const mergeTrophyLists = ( titleTrophies: Trophy[], earnedTrophies: Trophy[] ) => { const mergedTrophies: any[] = []; for (const earnedTrophy of earnedTrophies) { const foundTitleTrophy = titleTrophies.find( (t) => t.trophyId === earnedTrophy.trophyId ); mergedTrophies.push( normalizeTrophy({ ...earnedTrophy, ...foundTitleTrophy }) ); } return mergedTrophies; }; const normalizeTrophy = (trophy: Trophy) => { return { isEarned: trophy.earned ?? false, earnedOn: trophy.earned ? trophy.earnedDateTime : "unearned", type: trophy.trophyType, rarity: 0, earnedRate: Number(trophy.trophyEarnedRate), trophyName: trophy.trophyName, groupId: trophy.trophyGroupId }; }; main();
编译后的JavaScript代码
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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 (g && (g = 0, op[0] && (_ = 0)), _) 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 }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var fs = require("fs"); var psn_api_1 = require("psn-api"); function main() { return __awaiter(this, void 0, void 0, function () { var accessCode, authorization, allAccountsSearchResults, targetAccountId, trophyTitles, games, _i, trophyTitles_1, title, titleTrophies, earnedTrophies, mergedTrophies; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, (0, psn_api_1.exchangeNpssoForCode)("GqO4rje8Hx1sFqeEbzckLYND101S51LqWaXIrlMbC7TWybbZAYZygCqCSE8wx64E")]; case 1: accessCode = _a.sent(); return [4 /*yield*/, (0, psn_api_1.exchangeCodeForAccessToken)(accessCode)]; case 2: authorization = _a.sent(); return [4 /*yield*/, (0, psn_api_1.makeUniversalSearch)(authorization, "andykasen13", "SocialAllAccounts")]; case 3: allAccountsSearchResults = _a.sent(); targetAccountId = allAccountsSearchResults.domainResponses[0].results[0].socialMetadata .accountId; return [4 /*yield*/, (0, psn_api_1.getUserTitles)(authorization, targetAccountId)]; case 4: trophyTitles = (_a.sent()).trophyTitles; games = []; _i = 0, trophyTitles_1 = trophyTitles; _a.label = 5; case 5: if (!(_i < trophyTitles_1.length)) return [3 /*break*/, 9]; title = trophyTitles_1[_i]; return [4 /*yield*/, (0, psn_api_1.getTitleTrophies)(authorization, title.npCommunicationId, "all", { npServiceName: title.trophyTitlePlatform !== "PS5" ? "trophy" : undefined })]; case 6: titleTrophies = (_a.sent()).trophies; return [4 /*yield*/, (0, psn_api_1.getUserTrophiesEarnedForTitle)(authorization, targetAccountId, title.npCommunicationId, "all", { npServiceName: title.trophyTitlePlatform !== "PS5" ? "trophy" : undefined })]; case 7: earnedTrophies = (_a.sent()).trophies; mergedTrophies = mergeTrophyLists(titleTrophies, earnedTrophies); games.push({ gameName: title.trophyTitleName, platform: title.trophyTitlePlatform, trophyTypeCounts: title.definedTrophies, earnedCounts: title.earnedTrophies, trophyList: mergedTrophies }); _a.label = 8; case 8: _i++; return [3 /*break*/, 5]; case 9: // 7. Write to a JSON file. fs.writeFileSync("./games.json", JSON.stringify(games)); return [2 /*return*/]; } }); }); } var mergeTrophyLists = function (titleTrophies, earnedTrophies) { var mergedTrophies = []; var _loop_1 = function (earnedTrophy) { var foundTitleTrophy = titleTrophies.find(function (t) { return t.trophyId === earnedTrophy.trophyId; }); mergedTrophies.push(normalizeTrophy(__assign(__assign({}, earnedTrophy), foundTitleTrophy))); }; for (var _i = 0, earnedTrophies_1 = earnedTrophies; _i < earnedTrophies_1.length; _i++) { var earnedTrophy = earnedTrophies_1[_i]; _loop_1(earnedTrophy); } return mergedTrophies; }; var normalizeTrophy = function (trophy) { var _a; return { isEarned: (_a = trophy.earned) !== null && _a !== void 0 ? _a : false, earnedOn: trophy.earned ? trophy.earnedDateTime : "unearned", type: trophy.trophyType, rarity: 0, earnedRate: Number(trophy.trophyEarnedRate), trophyName: trophy.trophyName, groupId: trophy.trophyGroupId }; }; main();
我的tsconfig.json
{ "compilerOptions": { "target" : "ES6", "module": "CommonJS", "outDir": "./build", "strict": true } }
问题原因
你设置的target: ES6是核心问题——ES6(ES2015)标准里并没有async/await语法,这个特性是ES2017才正式纳入标准的。因此TypeScript会自动把async函数转成ES6兼容的模拟实现,也就是你看到的__awaiter、__generator这些辅助函数,用Promise和生成器逻辑来模拟async/await的行为。
解决方案
把tsconfig.json里的target字段修改为ES2017或更高版本(比如ESNext、ES2022),这样TypeScript就会直接输出原生的async/await语法,不再生成模拟代码。修改后的配置如下:
{ "compilerOptions": { "target" : "ES2017", "module": "CommonJS", "outDir": "./build", "strict": true } }
补充说明
当前主流的Node.js LTS版本(16.x及以上)都完全支持ES2017特性,无需担心兼容性问题。如果你的Node.js版本较老,建议先升级到最新LTS版本再使用该配置。
内容的提问来源于stack exchange,提问作者andy
相关产品推荐
相关产品推荐

