3992 words
20 minutes
TSConfig 生成(Emit)配置
2025-02-26 14:53:33
2025-12-24 23:45:46

← 返回 TSConfig 参考指南


声明文件生成 - declaration#

为项目中的每个 TypeScript 或 JavaScript 文件生成 .d.ts 文件。这些 .d.ts 文件是类型定义文件,用于描述模块的外部 API。通过 .d.ts 文件,TypeScript 等工具可以为未类型化的代码提供智能提示和准确的类型信息。

当 declaration 设置为 true 时,使用以下 TypeScript 代码运行编译器:

export let helloWorld = "hi";

将生成如下的 index.js 文件:

export let helloWorld = "hi";

同时会生成对应的 helloWorld.d.ts 文件:

export declare let helloWorld: string;

当处理 JavaScript 文件的 .d.ts 文件时,你可能需要使用 emitDeclarationOnly 或使用 outDir 来确保 JavaScript 文件不会被覆盖。

默认值:

  • 如果设置了 composite 则为 true;否则为 false

相关配置:

  • declarationDir
  • emitDeclarationOnly

发布版本:1.0

声明文件目录 - declarationDir#

提供一种配置声明文件输出根目录的方式。

示例目录结构:

example
├── index.ts
├── package.json
└── tsconfig.json

使用以下 tsconfig.json 配置:

{
  "compilerOptions": {
    "declaration": true,
    "declarationDir": "./types"
  }
}

将会把 index.ts 的 d.ts 文件放在 types 文件夹中:

example
├── index.js
├── index.ts
├── package.json
├── tsconfig.json
└── types
    └── index.d.ts

相关配置:

  • declaration

发布版本:2.0

声明文件映射 - declarationMap#

为 .d.ts 文件生成源映射文件,这些映射文件可以映射回原始的 .ts 源文件。这将允许像 VS Code 这样的编辑器在使用”转到定义”等功能时跳转到原始的 .ts 文件。

如果你正在使用项目引用(project references),强烈建议启用此选项。

发布版本:2.9

降级迭代 - downlevelIteration#

降级(Downleveling)是 TypeScript 中将代码转译为旧版本 JavaScript 的术语。此标志用于在旧版 JavaScript 运行时中启用对现代 JavaScript 迭代概念的更准确实现支持。

ECMAScript 6 添加了几个新的迭代原语:for / of 循环(for (el of arr))、数组展开([a, …b])、参数展开(fn(…args))和 Symbol.iterator。如果存在 Symbol.iterator 实现,downlevelIteration 允许在 ES5 环境中更准确地使用这些迭代原语。

对 for / of 的影响示例#

使用以下 TypeScript 代码:

const str = "Hello!";
for (const s of str) {
  console.log(s);
}

在未启用 downlevelIteration 时,任何对象的 for / of 循环都会被降级为传统的 for 循环:

"use strict";
var str = "Hello!";
for (var _i = 0, str_1 = str; _i < str_1.length; _i++) {
    var s = str_1[_i];
    console.log(s);
}

这通常是人们所期望的,但它并不是 100% 符合 ECMAScript 迭代协议。某些字符串,如表情符号(😜),的 .length 为 2(甚至更多!),但在 for-of 循环中应该作为 1 个单位进行迭代。

当启用 downlevelIteration 时,TypeScript 将使用一个辅助函数来检查是否存在 Symbol.iterator 实现(原生或 polyfill)。如果缺少此实现,将回退到基于索引的迭代。

"use strict";
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};

你可以通过 importHelpers 使用 tslib 来减少内联 JavaScript 的数量。

var e_1, _a;
var str = "Hello!";
try {
    for (var str_1 = __values(str), str_1_1 = str_1.next(); !str_1_1.done; str_1_1 = str_1.next()) {
        var s = str_1_1.value;
        console.log(s);
    }
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
    try {
        if (str_1_1 && !str_1_1.done && (_a = str_1.return)) _a.call(str_1);
    }
    finally { if (e_1) throw e_1.error; }
}

注意:如果运行时环境中不存在 Symbol.iterator,启用 downlevelIteration 并不会提高兼容性。

对数组展开的影响示例#

这是一个数组展开操作:

// 创建一个新数组,其元素是 1 后跟 arr2 的所有元素
const arr = [1, ...arr2];

根据描述,将其降级到 ES5 看起来很简单:

// 看起来是一样的,对吧?
const arr = [1].concat(arr2);

然而,在某些罕见情况下,这两种方式会有可观察到的差异。

例如,如果源数组缺少一个或多个项(包含空洞),展开语法会用 undefined 替换每个空项,而 .concat 会保持它们不变。

// 创建一个索引 1 处缺失元素的数组
let arrayWithHole = ["a", , "c"];
let spread = [...arrayWithHole];
let concatenated = [].concat(arrayWithHole);
console.log(arrayWithHole);
// [ 'a', <1 empty item>, 'c' ]
console.log(spread);
// [ 'a', undefined, 'c' ]
console.log(concatenated);
// [ 'a', <1 empty item>, 'c' ]

与 for / of 一样,如果存在 Symbol.iterator,downlevelIteration 将使用它来更准确地模拟 ES6 行为。

相关配置:

  • importHelpers

发布版本:2.3

生成 BOM - emitBOM#

控制 TypeScript 在写入输出文件时是否会生成字节顺序标记(BOM)。某些运行时环境需要 BOM 来正确解释 JavaScript 文件;而其他环境则要求不能有 BOM。除非你有特殊原因需要更改,否则默认值 false 通常是最佳选择。

发布版本:1.0

仅生成声明文件 - emitDeclarationOnly#

只生成 .d.ts 文件,不生成 .js 文件。

这个设置在以下两种情况下很有用:

  1. 你使用 TypeScript 以外的转译器来生成 JavaScript。
  2. 你使用 TypeScript 仅为你的使用者生成 d.ts 文件。

相关配置:

  • declaration

发布版本:2.8

导入辅助函数 - importHelpers#

对于某些降级操作,TypeScript 使用一些辅助代码来处理类的继承、数组或对象的展开以及异步操作。默认情况下,这些辅助函数会被插入到使用它们的文件中。如果在许多不同的模块中使用相同的辅助函数,这可能会导致代码重复。

如果启用 importHelpers 标志,这些辅助函数将从 tslib 模块中导入。你需要确保在运行时可以导入 tslib 模块。这只影响模块;全局脚本文件不会尝试导入模块。

例如,对于这段 TypeScript 代码:

export function fn(arr: number[]) {
  const arr2 = [1, ...arr];
}

当启用 downlevelIteration 但 importHelpers 为 false 时:

var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
export function fn(arr) {
    var arr2 = __spreadArray([1], __read(arr), false);
}

当同时启用 downlevelIteration 和 importHelpers 时:

import { __read, __spreadArray } from "tslib";
export function fn(arr) {
    var arr2 = __spreadArray([1], __read(arr), false);
}

当你提供了这些函数的自己的实现时,可以使用 noEmitHelpers。

相关配置:

  • noEmitHelpers
  • downlevelIteration

发布版本:2.1


内联源映射 - inlineSourceMap#

当设置此选项时,TypeScript 不会生成单独的 .js.map 文件来提供源映射,而是将源映射内容直接嵌入到 .js 文件中。虽然这会导致 JS 文件变大,但在某些场景下会很方便。例如,当你需要在不允许服务 .map 文件的网络服务器上调试 JS 文件时。

此选项与 sourceMap 互斥。

例如,对于这段 TypeScript 代码:

const helloWorld = "hi";
console.log(helloWorld);

转换为这段 JavaScript 代码:

"use strict";
const helloWorld = "hi";
console.log(helloWorld);

当启用 inlineSourceMap 构建时,文件底部会包含一个注释,其中包含了该文件的源映射:

"use strict";
const helloWorld = "hi";
console.log(helloWorld);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDO0FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMifQ==

发布版本:1.5

内联源码 - inlineSources#

当设置此选项时,TypeScript 会将原始 .ts 文件的内容作为嵌入字符串包含在源映射中(使用源映射的 sourcesContent 属性)。这通常在与 inlineSourceMap 相同的场景下很有用。

需要设置 sourceMap 或 inlineSourceMap 才能使用此选项。

例如,对于这段 TypeScript 代码:

const helloWorld = "hi";
console.log(helloWorld);

默认情况下转换为这段 JavaScript 代码:

"use strict";
const helloWorld = "hi";
console.log(helloWorld);

当同时启用 inlineSources 和 inlineSourceMap 构建时,文件底部会包含一个注释,其中包含了该文件的源映射。注意,这里的结尾与 inlineSourceMap 示例中的不同,因为源映射现在还包含了原始源代码:

"use strict";
const helloWorld = "hi";
console.log(helloWorld);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDO0FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBoZWxsb1dvcmxkID0gXCJoaVwiO1xuY29uc29sZS5sb2coaGVsbG9Xb3JsZCk7Il19

发布版本:1.5

映射根目录 - mapRoot#

指定调试器应该查找映射文件的位置,而不是生成的位置。这个字符串会在源映射中按原样处理,例如:

{
  "compilerOptions": {
    "sourceMap": true,
    "mapRoot": "https://my-website.com/debug/sourcemaps/"
  }
}

这将声明 index.js 的源映射位于 https://my-website.com/debug/sourcemaps/index.js.map。

发布版本:1.0

换行符 - newLine#

指定生成文件时使用的行尾序列:‘CRLF’(dos)或 ‘LF’(unix)。

默认值:

  • lf

允许值:

  • crlf
  • lf

发布版本:1.5

不生成输出 - noEmit#

不生成编译器输出文件,如 JavaScript 源代码、源映射或声明文件。

这为其他工具(如 Babel 或 swc)提供了空间,让它们来处理将 TypeScript 文件转换为可以在 JavaScript 环境中运行的文件。

你可以将 TypeScript 用作提供编辑器集成的工具,以及源代码类型检查器。

发布版本:1.5


不生成辅助函数 - noEmitHelpers#

不使用 importHelpers 导入辅助函数,而是在全局作用域中提供你所使用的辅助函数的实现,并完全关闭辅助函数的生成。

例如,在 ES5 中使用这个异步函数需要一个类似 await 的函数和类似生成器的函数来运行:

const getAPI = async (url: string) => {
  // Get API
  return {};
};

这会生成相当多的 JavaScript 代码:

"use strict";
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 = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["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 };
    }
};
var getAPI = function (url) { return __awaiter(void 0, void 0, void 0, function () {
    return __generator(this, function (_a) {
        // Get API
        return [2 /*return*/, {}];
    });
}); };

通过这个标志,你可以使用自己的全局实现来替换这些辅助函数:

"use strict";
var getAPI = function (url) { return __awaiter(void 0, void 0, void 0, function () {
    return __generator(this, function (_a) {
        // Get API
        return [2 /*return*/, {}];
    });
}); };

相关配置:

  • importHelpers

发布版本:1.5

有错误时不生成输出 - noEmitOnError#

如果报告了任何错误,不生成编译器输出文件,如 JavaScript 源代码、源映射或声明文件。

默认值为 false,这使得在类似监视的环境中更容易使用 TypeScript,在这种环境中,你可能希望在确保所有错误都已解决之前,在另一个环境中查看代码更改的结果。

发布版本:1.4

输出目录 - outDir#

如果指定了此选项,.js 文件(以及 .d.ts、.js.map 等文件)将被生成到这个目录中。原始源文件的目录结构会被保留;如果计算出的根目录不是你想要的,请参见 rootDir。

如果未指定,.js 文件将在与生成它们的 .ts 文件相同的目录中生成:

$ tsc
example
├── index.js
└── index.ts

使用这样的 tsconfig.json:

{
  "compilerOptions": {
    "outDir": "dist"
  }
}

使用这些设置运行 tsc 会将文件移动到指定的 dist 文件夹中:

$ tsc
example
├── dist
   └── index.js
├── index.ts
└── tsconfig.json

相关配置:

  • out
  • outFile

发布版本:1.0


保留常量枚举 - preserveConstEnums#

不在生成的代码中擦除 const enum 声明。const enums 通过在编译时将枚举值替换为实际值而不是引用,提供了一种减少应用程序运行时内存占用的方法。

例如,对于这段 TypeScript 代码:

const enum Album {
  JimmyEatWorldFutures = 1,
  TubRingZooHypothesis = 2,
  DogFashionDiscoAdultery = 3,
}

const selectedAlbum = Album.JimmyEatWorldFutures;
if (selectedAlbum === Album.JimmyEatWorldFutures) {
  console.log("That is a great choice.");
}

const enum 的默认行为是将任何 Album.Something 转换为相应的数字字面量,并从 JavaScript 中完全移除对枚举的引用:

"use strict";
const selectedAlbum = 1 /* Album.JimmyEatWorldFutures */;
if (selectedAlbum === 1 /* Album.JimmyEatWorldFutures */) {
    console.log("That is a great choice.");
}

当 preserveConstEnums 设置为 true 时,枚举在运行时依然存在,并且数字值仍然会被生成:

"use strict";
var Album;
(function (Album) {
    Album[Album["JimmyEatWorldFutures"] = 1] = "JimmyEatWorldFutures";
    Album[Album["TubRingZooHypothesis"] = 2] = "TubRingZooHypothesis";
    Album[Album["DogFashionDiscoAdultery"] = 3] = "DogFashionDiscoAdultery";
})(Album || (Album = {}));
const selectedAlbum = 1 /* Album.JimmyEatWorldFutures */;
if (selectedAlbum === 1 /* Album.JimmyEatWorldFutures */) {
    console.log("That is a great choice.");
}

这本质上使得这样的 const enums 仅作为源代码特性存在,在运行时不留下任何痕迹。

默认值:

  • 如果设置了 isolatedModules 则为 true;否则为 false

发布版本:1.4

移除注释 - removeComments#

在转换为 JavaScript 时从 TypeScript 文件中删除所有注释。默认值为 false。

例如,这是一个带有 JSDoc 注释的 TypeScript 文件:

/** The translation of 'Hello world' into Portuguese */
export const helloWorldPTBR = "Olá Mundo";

当 removeComments 设置为 true 时:

export const helloWorldPTBR = "Olá Mundo";

当未设置 removeComments 或设置为 false 时:

/** The translation of 'Hello world' into Portuguese */
export const helloWorldPTBR = "Olá Mundo";

这意味着你的注释将会出现在 JavaScript 代码中。

发布版本:1.0

源映射 - sourceMap#

启用源映射文件的生成。这些文件允许调试器和其他工具在实际处理生成的 JavaScript 文件时显示原始的 TypeScript 源代码。源映射文件作为 .js.map(或 .jsx.map)文件生成在相应的 .js 输出文件旁边。

.js 文件反过来会包含一个源映射注释,向外部工具指示文件的位置,例如:

// helloWorld.ts
export declare const helloWorld = "hi";

使用 sourceMap 设置为 true 编译会创建以下 JavaScript 文件:

// helloWorld.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.helloWorld = "hi";
//# sourceMappingURL=// helloWorld.js.map

同时还会生成这个 json 映射:

// helloWorld.js.map
{
  "version": 3,
  "file": "ex.js",
  "sourceRoot": "",
  "sources": ["../ex.ts"],
  "names": [],
  "mappings": ";;AAAa,QAAA,UAAU,GAAG,IAAI,CAAA"
}

发布版本:1.0

源根目录 - sourceRoot#

指定调试器应该在哪里查找 TypeScript 文件,而不是使用相对源位置。这个字符串在源映射中会被按原样处理,你可以使用路径或 URL:

{
  "compilerOptions": {
    "sourceMap": true,
    "sourceRoot": "https://my-website.com/debug/source/"
  }
}

这将声明 index.js 的源文件位于 https://my-website.com/debug/source/index.ts。

发布版本:1.0

移除内部声明 - stripInternal#

不为在其 JSDoc 注释中带有 @internal 注解的代码生成声明。这是一个内部编译器选项;使用时需要自担风险,因为编译器不会检查结果是否有效。如果你正在寻找一个工具来处理 d.ts 文件中的额外可见性级别,请查看 api-extractor。

/**
 * Days available in a week
 * @internal
 */
export const daysInAWeek = 7;

/** Calculate how much someone earns in a week */
export function weeklySalary(dayRate: number) {
  return daysInAWeek * dayRate;
}

当标志设置为 false(默认值)时:

/**
 * Days available in a week
 * @internal
 */
export declare const daysInAWeek = 7;
/** Calculate how much someone earns in a week */
export declare function weeklySalary(dayRate: number): number;

当 stripInternal 设置为 true 时,生成的 d.ts 将被编辑:

/** Calculate how much someone earns in a week */
export declare function weeklySalary(dayRate: number): number;

JavaScript 输出仍然保持不变。

发布版本:1.5

TSConfig 生成(Emit)配置
https://0bipinnata0.my/posts/typescript/tsconfig/03-emit/
Author
0bipinnata0
Published at
2025-02-26 14:53:33