Skip to content

tsconfig.json配置讲解

关于 tsconfig.json 的相关详细配置参考官方文档说明:https://www.typescriptlang.org/tsconfig/#Type_Checking_6248

一、tsconfig.json配置

如果一个目录下存在一个 tsconfig.json 文件,那么它意味着这个目录是 TypeScript 项目的根目录。 tsconfig.json 文件中指定了用来编译这个项目的根文件和编译选项。 一个项目可以通过以下方式之一来编译:

  • 不带任何输入文件的情况下调用 tsc 命令,编译器会从当前目录开始去查找 tsconfig.json 文件,逐级向上搜索父目录。

  • 不带任何输入文件的情况下调用 tsc,且使用命令行参数 --project(或 -p)指定一个包含 tsconfig.json 文件的目录。

    当命令行上指定了输入文件时,tsconfig.json 文件会被忽略。

1.1、files 字段

"files"指定一个包含相对或绝对文件路径的列表

🔔注意:只能文件,不能是文件夹

1.2、include

include 字段用于指明需要被 tsc 编译的文件或文件夹列表,例如

json
{
  "include": ["src", "global.d.ts"]
}

1.3、exclude

exclude 字段用于排除不需要 tsc 编译的文件或文件夹列表,例如:

json
{
  "exclude": ["test.ts", "src/test.ts"],
}

1.4、include和exclude的细节

"include""exclude" 属性指定一个文件 glob 匹配模式列表。 支持的 glob 通配符有

  • * 匹配0或多个字符(不包括目录分隔符)
  • ? 匹配一个任意字符(不包括目录分隔符)
  • **/ 递归匹配任意子目录
  1. 如果一个 glob 模式里的某部分只包含 *.*,那么仅有支持的文件扩展名类型被包含在内(比如默认 .ts,.tsx,和 .d.ts, 如果 allowJs 设置能 true 还包含 .js 和 .jsx )

  2. 如果 "files""include" 都没有被指定,编译器默认包含当前目录和子目录下所有的 TypeScript 文件( .ts, .d.ts.tsx ),排除在 "exclude" 里指定的文件。JS文件( .js.jsx )也被包含进来如果 allowJs 被设置成 true。 如果指定了 "files""include",编译器会将它们结合一并包含进来。 使用 "outDir" 指定的目录下的文件永远会被编译器排除,除非你明确地使用 "files" 将其包含进来(这时就算用 exclude 指定也没用)

  3. 使用 "include" 引入的文件可以使用 "exclude" 属性过滤。 然而,通过 "files" 属性明确指定的文件却总是会被包含在内,不管 "exclude" 如何设置。 如果没有特殊指定, "exclude" 默认情况下会排除 node_modulesbower_componentsjspm_packages<outDir> 目录

  4. 任何被 "files""include" 指定的文件所引用的文件也会被包含进来。 A.ts 引用了 B.ts,因此 B.ts 不能被排除,除非引用它的 A.ts"exclude" 列表中。

  5. tsconfig.json 文件可以是个空文件,那么所有默认的文件(如上面所述)都会以默认配置选项编译。

1.5、extends

tsconfig.json 文件可以利用 extends 属性从另一个配置文件里继承配置。

extends 是 tsconfig.json 文件里的顶级属性(与 compilerOptions,files,include,和 exclude 一样)。 extends 的值是一个字符串,包含指向另一个要继承文件的路径。

1.6、compileOnSave

compileOnSave 是声明是否需要在保存时候自动触发 tsc 编译的字段,一般来说,我们的代码编译过程会通过 Rollup、Webpack 等打包构建工具,并且使用热更新,因此无需配置该项,保持缺省即可。

json
{
  "compileOnSave": false,
}

1.7、compilerOptions

compilerOptions 为编译选项,详细可以参考下一章

二、compilerOptions选项详解

2.1、类型检查相关配置

2.1.1、strict

当 strict 为 true 时,会默认开启一系列相关类型检查行为。相互关联的配置项有

如果需要关闭单个配置,就单独设置该项为 false 即可,例如

json
  "compilerOptions": {
    // 类型检查相关配置
    "strict": true
    "alwaysStrict": false
  },

2.1.2、alwaysStrict

alwaysStrict 选项告诉 TypeScript 编译器以严格模式解析所有源代码,并为每个生成的 JavaScript 文件在顶部添加 "use strict"; 指令

示例代码

typescript
function example() {
  // 在严格模式下,这会导致编译错误
  undeclaredVar = "hello"; // 错误:找不到名称 'undeclaredVar'
  
  // 在严格模式下,这会导致编译错误
  const obj = { x: 1 };
  with (obj) { // 错误:严格模式禁止使用 'with' 语句
    console.log(x);
  }
}

example();

会被编译成

javascript
"use strict";
function example() {
    // 在严格模式下,这会导致运行时错误
    undeclaredVar = "hello"; // ReferenceError: undeclaredVar is not defined
}
example();

2.1.3、strictNullChecks

strictNullChecksfalse 时,语言实际上忽略了 nullundefined。这可能会导致运行时出现意外错误。

strictNullCheckstrue 时,nullundefined 有自己的不同类型,如果您尝试在需要具体值的地方使用它们,您将收到类型错误。

例如:以下代码中的 users.find 不能保证它真的会找到一个用户,但你可以编写代码

typescript
declare const loggedInUsername: string;

const users = [
  { name: 'Oby', age: 12 },
  { name: 'Heera', age: 32 },
];

const loggedInUser = users.find((u) => u.name === loggedInUsername);
console.log(loggedInUser.age);

strictNullChecks 设置为 true 将引发一个错误,表明您在尝试使用它之前尚未保证 loggedInUser 存在。

image-20250910183853232

strictNullChecks 设置为 false 时则不会提示,可能会导致运行时出现意外错误。

2.1.4、strictBindCallApply

设置后,TypeScript 将检查函数 callbindapply 的内置方法是否使用底层函数的正确参数调用,示例如下

typescript
function fn(x: string) {
  return parseInt(x);
}
 
const n1 = fn.call(undefined, "10");
 
const n2 = fn.call(undefined, false);

image-20250910192700070

2.1.5、strictBuiltinIteratorReturn

typescript
const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();

// 正常迭代
console.log(iterator.next()); // { value: 1, done: false }

// 调用 return 方法用于提前终止迭代 - 这里类型检查不够严格
const result = iterator.return(); // error

console.log(result); // { value: undefined, done: true }

2.1.6、strictFunctionTypes

typescript
function fn(x: string) {
  console.log('Hello, ' + x.toLowerCase());
}

type StringOrNumberFunc = (ns: string | number) => void;

/**
 * 不能将类型“{ (x: string): number; (x: string): void; (x: string): void; }”分配给类型“StringOrNumberFunc”。
  参数“x”和“ns” 的类型不兼容。
    不能将类型“string | number”分配给类型“string”。
      不能将类型“number”分配给类型“string”。
 */
let func: StringOrNumberFunc = fn; // error

func(10);

/**
 * 当 strictFunctionTypes 启用时
 * 上述写法可以改成以下写法
 */
type Methodish = {
  func(x: string | number): void;
};

function fn(x: string) {
  console.log('Hello, ' + x.toLowerCase());
}

// Ultimately an unsafe assignment, but not detected
const m: Methodish = {
  func: fn,
};
m.func(10);

2.1.7、strictPropertyInitialization

当设置为 true 时,当类属性已声明但未在构造函数中设置时,TypeScript 将引发错误。

typescript
class UserAccount {
  name: string;
  accountType = 'user';

  email: string;
  address: string | undefined;

  constructor(name: string) {
    this.name = name;
    // Note that this.email is not set
  }
}

image-20250910192711186

上述的情况 name 和 email 是必填的,所以需要在 constructor 中进行设置

2.1.8、noImplicitAny

在某些情况下,如果不存在类型注释,当它无法推断该类型时,TypeScript 将回退到变量的 any 类型。

typescript
function fn(s) {
  console.log(s.subtr(3));
}
fn(42);

上述代码并没有什么错误,当 noImplicitAny 设置为 true 时就会提示 s 参数类型为隐式 any

image-20250910193637821

2.1.9、noImplicitThis

在隐含 “any” 类型的 “this” 表达式上引发错误,示例如下

typescript
class Rectangle {
  width: number;
  height: number;

  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }

  getAreaFunction() {
    return function () {
      return this.width * this.height;
    };
  }
}

下面的类返回一个尝试访问 this.widththis.height 的函数,但 getAreaFunction 内部 函数中的上下文 不是 Rectangle 的实例

image-20250910193906383

2.1.10、useUnknownInCatchVariables

在 TypeScript 4.0 中,添加了支持以允许将 catch 子句中变量的类型从 any 更改为 unknown。允许以下代码:

typescript
try {
  // ...
} catch (err: unknown) {
  // We have to verify err is an
  // error before using it as one.
  if (err instanceof Error) {
    console.log(err.message);
  }
}

2.1.11、allowUnreachableCode

allowUnreachableCode 表示是否允许无法访问的代码,为 true 时表示忽略,false 时引发有关无法访问代码的编译器错误,默认为 undefined 向编辑者提供建议作为警告

示例如下

typescript
function fn(n: number) {
  if (n > 5) {
    return true;
  } else {
    return false;
  }
  return true;
}

其中函数内的最后一行执行代码 return true; 代码始终无法执行到该行代码

image-20250910195622286

2.1.12、allowUnusedLabels

allowUnusedLabels 表示是否允许未使用的标签,为 true 时表示忽略,false 时引发有关无法访问代码的编译器错误,默认为 undefined 向编辑者提供建议作为警告

首先需要先介绍以下 label 语法,JavaScript 是存在 label 语法的,像以下示例一样

javascript
function exampleWithUnusedLabel() {
  unusedLabel: for (let i = 0; i < 5; i++) {
    console.log(i);
  }
}

function anotherExample() {
  myLabel: {
    console.log("This is a block");
  }
}

像上述中的 exampleWithUnusedLabel 和 myLabel 表示其中的代码块含义,更加详细的了解可自行参考其他文档深入了解,这里不做详细描述。

像这里设置 allowUnusedLabels 为 false 时就会提示,exampleWithUnusedLabel 并未被其他地方引用

image-20250910200205868

2.1.13、exactOptionalPropertyTypes

exactOptionalPropertyTypes 选项让 TypeScript 对可选属性进行更严格的检查。启用后,可选属性只能被省略或赋值为明确类型,不能显式赋值为 undefined

typescript
interface User {
  name: string;
  age?: number;
}

const user1: User = { name: 'Alice' }; // 省略 optional
const user2: User = { name: 'Jack', age: 42 }; // 赋值为 number

// 当 exactOptionalPropertyTypes 为 false 时是允许的,为 true 则会提示错误
const user3: User = { name: 'black', age: undefined }; // 赋值为 undefined

image-20250910233307882

2.1.14、noFallthroughCasesInSwitch

noFallthroughCasesInSwitch 选项用于防止在 switch 语句中出现意外的 case 穿透(fallthrough)行为。当启用时(推荐),TypeScript 会检查每个 case 块是否以 break、return 或 throw 等语句结束,如果没有,就会报错。设置为 true 表示开启,false 为关闭。示例如下

typescript
function getFruitPrice(fruit: string): number {
  let price = 0;

  switch (fruit) {
    case 'apple':
      price = 2; // 错误:这里没有 break、return 或 throw
    case 'banana':
      price = 1;
      break;
    default:
      price = 0;
  }

  return price;
}

image-20250910225126373

2.1.15、noImplicitOverride

当使用继承类时,子类可能会覆盖基类中的方法,覆盖方法前需要加 override 修饰符,示例如下

typescript
class Album {
  download() {
    // Default behavior
  }
}

class SharedAlbum extends Album {
  download() {
    // Override to get info from many sources
  }
}

当 noImplicitOverride 设置为 true 时(默认为 false),会提示以下错误

image-20250910201316868

2.1.16、noImplicitReturns

启用后,TypeScript 将检查函数中的所有代码路径,以确保它们返回值

typescript
function lookupHeadphonesManufacturer(color: 'blue' | 'black'): string {
  if (color === 'blue') {
    return 'beats';
  } else {
    ('bose');
  }
}

image-20250910221921655

2.1.17、noUncheckedIndexedAccess

noUncheckedIndexedAccess 选项让 TypeScript 对数组和索引签名属性的访问更加严格。启用后,通过索引访问数组元素或对象动态属性时,返回的类型会包含 undefined,强制你处理可能不存在的值。示例如下

typescript
const numbers = [1, 2, 3, 4, 5];

const first = numbers[0];
const tenth = numbers[9];

当 noUncheckedIndexedAccess 为 false 不启用时

image-20250910231413181

当 noUncheckedIndexedAccess 为 true 启用时

image-20250910231510430

2.1.18、noPropertyAccessFromIndexSignature

noPropertyAccessFromIndexSignature 选项当访问已知属性使用 . 符号,访问索引属性使用方括号 [] 进行访问。这样可以更清楚地区分已知属性和动态属性。示例如下

typescript
interface Config {
  // 已知属性
  timeout: number;
  retries: number;

  // 索引签名(动态属性)
  [key: string]: number;
}

const config: Config = {
  timeout: 1000,
  retries: 3,
  customDelay: 500, // 动态属性
};

console.log(config.timeout); // ✅ 1000 - 点符号访问已知属性
console.log(config['timeout']); // ✅ 1000 - 方括号访问已知属性
console.log(config.customDelay); // ✅ 500 - 点符号访问动态属性(但有问题!)
console.log(config['customDelay']); // ✅ 500 - 方括号访问动态属性

image-20250910230430496

2.1.19、noUnusedLocals

报告未使用的局部变量的错误,为 true 时启用,false 为关闭

typescript
const createKeyboard = (modelID: number) => {
  const defaultModelID = 23;
  return { type: 'keyboard', modelID };
};

image-20250910222224729

2.1.20、noUnusedParameters

报告函数中未使用参数的错误,为 true 时启用,false 为关闭

typescript
const createDefaultKeyboard = (modelID: number) => {
  const defaultModelID = 23;
  return { type: 'keyboard', modelID: defaultModelID };
};

image-20250910222605297

2.1.21、类型检查总结

strictPropertyInitialization 和 exactOptionalPropertyTypes 配置项需要先指定 strictNullChecks 的配置,否则配置不生效

image-20250910235236450

2.2、代码内核心配置

2.2.1、lib

TypeScript 包括一组内置 JS API(如 Math)的默认类型定义,以及浏览器环境中的事物(如 document )的类型定义。TypeScript 还包括与您指定的 target 匹配的较新 JS 功能的 API,更新详细配置可以参考 https://www.typescriptlang.org/tsconfig/#lib

当 lib 只设置以下时,当代码中使用到 document API 和 ES6 以上的新特性API 就会提示错误

json
{
  "compilerOptions": {
    "lib": ["ES2015"]
  },
}

image-20250911111115627

2.2.2、target

target 选项指定编译后的 JavaScript 版本,可以设置的值有 es3、es5、es6/es2015、es2016、es2017、es2018、es2019、es2020、es2021、es2022、es2023、es2024、esnext

2.2.3、module

module 指定编译后的所运行环境的模块系统,设置的值可以为 none、commonjs、amd、umd、system、es6/es2015、es2020、es2022、esnext、node16、node18、node20、nodenext、preserve

其中 commonjs、amd、umd、system、分别表示各自的模块系统。es6/es2015 表示 ESM 模块系统,es2020 除了 es6/es2015 的基本功能外,es2020 还增加了对 dynamic import and import.meta 而 es2022 进一步增加了对 top level await 的支持

node16 支持 ES Modules (ESM) 和 CommonJS (CJS) 两种模块系统。在 Node.js 16 中,你可以通过在 package.json 中设置 "type": "module" 来启用 ESM,或者在文件扩展名上使用 .mjs 来明确表示文件为 ESM。默认情况下,如果未设置 "type" 或文件扩展名为 .cjs,则 Node.js 使用 CommonJS

而 node18 和 node20 同样支持 ESM 和 CJS ,并且在模块方面进行了进一步的优化和改进而已。

exnext 和 nodenext 表示默认启用下个稳定版本

2.2.4、moduleResolution

指定模块解析策略,可以允许的值为 classic、node10/node、node16、nodenext、bundler

  1. 如果 module 选项设置为 commonjs,则 moduleResolution 应该设置为 node10 和 node 作用是一样的,仅支持用于 v10 之前的 Node.js 版本,只支持 CommonJS 模块

  2. 如果 module 选项设置为 node16、node18 和 node20,则 moduleResolution 应该设置为 Node16

  3. 如果 module 选项设置为 nodenext,则 moduleResolution 应该设置为 nodenext

  4. 如果 module 选项设置为 preserve 或 es2015 或更高版本,则 moduleResolution 应该设置为 bundler 否则则为 classic

2.2.5、baseUrl和path

json
{
  "baseUrl": ".", // 设置一个基本目录
  "paths": {
    // 配置别名路径
    "@/*": ["src/*"]
  }
}
typescript
import { add } from '@/utils/math';

2.2.6、importHelpers

importHelpers 选项主要和 tslib 配合起来使用,当 target 选项设置为 es5 时,但是在代码中会用到 async 和 await 相关语法。tsc 进行编译时会处理兼容性 "importHelpers": true,

json
{
  "compilerOptions": {
    "target": "es5", // 指定编译后的 JavaScript 版本
    "lib": ["DOM", "ES2024"],
  },
  "include": ["src/**/*.ts", "globals.d.ts"],
  "exclude": ["node_modules"]
}

实际的使用代码

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

当 importHelpers 不使用时,进行编译时查看结果

image-20250911140255557

当 importHelpers 启用时,会提示需要按照 tslib

image-20250911140355671

然后需要安装 tslib,并且 tslib 是运行时使用,使用 --save 进行安装

bash
npm i tslib --save

然后再次编译,其中 __awaiter 和 __generator 会从 tslib 辅助函数中进行导入

image-20250911140626429

与该选项相关的配置为 noEmitHelpers ,主要就是取消对 tslib 帮助程序的使用,详细见:https://www.typescriptlang.org/tsconfig/#noEmitHelpers。

2.2.7、esModuleInterop 和 allowSyntheticDefaultImports

默认情况下(esModuleInterop 为 false 或未设置)TypeScript 将 CommonJS/AMD/UMD 模块视为 ES6 模块。

当 module 选项设置为 node16、nodenext、preserve 时,esModuleInterop 默认开启 true,否则则为 false

当 esModuleInterop 选项启动时默认也会启用 allowSyntheticDefaultImports 选项

allowSyntheticDefaultImports 选项则是允许合成默认导入

这里先配置以下设置

json
{
  "compilerOptions": {
    "target": "es5", // 指定编译后的 JavaScript 版本
    "lib": ["DOM", "ES2024"],
    "outDir": "dist",
    "module": "commonjs", // 指定编译后的所运行环境的模块系统,不会开启 esModuleInterop
    "moduleResolution": "node10"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}

在实际代码上,会提示模块 "node:fs" 没有默认导出。

typescript
import fs from 'node:fs';

fs.copyFile('', '', () => {});

image-20250911172603601

原因是 fs 模块是 CommonJS 方案实现,没有默认导出,此时有两个方案

第一种是通过以下方式进行解决

typescript
import * as fs from 'node:fs';

fs.copyFile('', '', () => {});

console.log('执行');

编译后的代码依然可以执行

js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var fs = require("node:fs");
fs.copyFile('', '', function () { });
console.log('执行');

第二种是通过 esModuleInterop,但是这一边我先不开启 esModuleInterop,先设置启用 allowSyntheticDefaultImports

json
{
  "allowSyntheticDefaultImports": true
}

Typescript 并不会报告错误

image-20250911173401868

allowSyntheticDefaultImports 会在编译后的模块添加 default 属性,tsc 虽然可以编译成功但是编译后的代码无法执行

image-20250911173659843

当在设置 esModuleInterop 时,编译之后会多出 __importDefault 辅助函数,并且编译后的代码可以执行

image-20250911173856828

当设置 esModuleInterop 并且还使用以下方式还会多出其他辅助函数

javascript
import * as fs from 'node:fs';

fs.copyFile('', '', () => {});

console.log('执行');

image-20250911174156451

当 module 选项设置为以下设置时

json
{
  "module": "es2022", // 指定编译后的所运行环境的模块系统
	"moduleResolution": "bundler",
}
// 或
{
  "module": "node16", // 指定编译后的所运行环境的模块系统,会默认开启 esModuleInterop allowSyntheticDefaultImports
	"moduleResolution": "node16",
}

当前代码中 无论使用以下那种方式都正常编译

typescript
import * as fs from 'node:fs';
// or
import fs from 'node:fs';

2.3、jsx 相关设置

控制 JSX 构造在 JavaScript 文件中的发布方式。这仅影响以 .tsx 文件开始的 JS 文件的输出。

示例如下

tsx
export const HelloWorld = () => <h1>Hello world</h1>;

当 jsx 设置 react-jsx 时,编译之后

jsx
import { jsx as _jsx } from "react/jsx-runtime";
export const HelloWorld = () => _jsx("h1", { children: "Hello world" });

当 jsx 设置 react-jsxdev 时,编译之后

jsx
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
const _jsxFileName = "/home/runner/work/TypeScript-Website/TypeScript-Website/packages/typescriptlang-org/index.tsx";
export const HelloWorld = () => _jsxDEV("h1", { children: "Hello world" }, void 0, false, { fileName: _jsxFileName, lineNumber: 9, columnNumber: 32 }, this);

当 jsx 设置 preserve 时,编译之后

jsx
import React from 'react';
export const HelloWorld = () => <h1>Hello world</h1>;

当 jsx 设置 react-native 时,编译之后

jsx
import React from 'react';
export const HelloWorld = () => <h1>Hello world</h1>;

当 jsx 设置 react 时,编译之后

jsx
import React from 'react';
export const HelloWorld = () => React.createElement("h1", null, "Hello world");

2.4、其他相关配置

2.4.1、outFile

如果 modulesystemamd,则所有模块文件也将在所有全局内容之后连接到此文件中。

🔔注意:除非 moduleNoneSystemAMD,否则不能使用 outFile。此选项不能用于 CommonJS 或 ES6 模块。

2.4.2、是否启用装饰器

json
{
  "compilerOptions": {
    "emitDecoratorMetadata": true, 
    "experimentalDecorators": true,
  },
}

2.4.3、其他相关常用配置

json
{
  "compilerOptions": {
    "target": "es5", // 指定编译后的 JavaScript 版本
    "lib": ["DOM", "ES2024"],
    "outDir": "dist", // 文件输出目录
    "preserveConstEnums": true, // 是否禁止删除枚举常量生成代码中的声明,默认为 false
    "skipLibCheck": true, // 跳过类型声明文件的检查
    "declaration": true, // 是否为每个 Typescript 文件生成 .d.ts 文件
    "declarationDir": "./types", // 将生成的 .d.ts 指定目录
    "noEmit": false, // 不输入编译后的文件
    "noEmitOnError": false, // 若 Typescript 报告了任何错误就不会输出编译后的文件
    "sourceMap": true, // 开启 sourceMap
    "removeComments": false, // 是否移除注释
    "allowJs": true, // 是否对js文件进行编译,默认:false
    "checkJs": false // 是否检查js代码是否符合语法规范,当使用checkJs,必须使用allowJs,默认:false
  },
  "include": ["src/**/*.ts", "globals.d.ts"],
  "exclude": ["node_modules"]
}