08 tsconfig 全量参考

按类别分表的编译选项参考、strict 逐项拆解、TypeScript 6.0 废弃清单与可直接抄的配置模板

08 tsconfig 全量参考

tsconfig.json 是 TypeScript 项目里影响最大、又最容易抄错的文件。本页按官方分类组织,重点是6.0 的变化。

本页所有「默认值」「报错码」「选项接受性」均在 TypeScript 6.0.3 用 tsc --showConfig 与真实编译实测确认。

官方完整选项列表:typescriptlang.org/tsconfig 📘


TypeScript 6.0:先看这一段

6.0 是通往 7.0(Go 原生编译器)的桥梁,主要目的就是对齐 7.0 行为。升级时最容易踩的三类问题:

一、新默认值(会静默改变行为)

选项5.x 默认6.0 默认后果
strictfalsetrue 🔥升级后可能一次爆出大量错误
targetES5 / 浮动ES2025(LatestStandard)产物语法更现代
jsx—preserve保留 JSX
types自动加载全部 @types/*[] ⚠️不再自动加载 @types/node
rootDir从输入文件推断不推断产物路径可能多一层

二、两个高频「升级即报错」的症状

1
error TS2304: Cannot find name 'process'.

→ types 默认为 [] 了。加:

1
{ "compilerOptions": { "types": ["node"] } }
1
产物出现在 dist/src/index.js 而不是 dist/index.js

→ rootDir 不再推断。显式声明:

1
{ "compilerOptions": { "rootDir": "./src", "outDir": "./dist" } }

三、废弃选项(报错,不是警告)

🗑️ 废弃报错码替代方案
target: "ES5"TS5107最低 ES2015;真要 ES5 得换编译器
moduleResolution: "node" / "node10"TS5107"bundler"(打包器)或 "nodenext"(Node)
module: "amd" / "umd" / "systemjs" / "none"TS5107ESM + 打包器
baseUrlTS5101前缀折叠进每条 paths
outFileTS5101外部打包器(esbuild / Rollup / Vite)
downlevelIterationTS5101只在 ES5 产物下有意义,删掉
esModuleInterop: falseTS5107互操作恒定开启,删掉该行
allowSyntheticDefaultImports: falseTS5107同上
alwaysStrict: falseTS5107所有代码恒为严格模式
import ... assert { }TS2880改用 with { type: "json" }
/// <reference no-default-lib="true"/>🗑️noLib / libReplacement

临时续命(仅作迁移过渡,7.0 彻底移除):

1
{ "compilerOptions": { "ignoreDeprecations": "6.0" } }

⚠️ ignoreDeprecations 是临时止痛药,不是解决方案。正确用法:① 先加上让项目能构建 → ② 修完新默认值问题 → ③ 逐个替换废弃选项 → ④ 删掉它。删掉后能干净构建,就说明项目已为 7.0 做好准备。


严格性选项(Type Checking)

strict 是一个开关组。它只包含下面这些(官方 optionDeclarations 分类实测):

受 strict 控制的选项检查什么典型报错
noImplicitAny隐式 anyTS7006
strictNullChecksnull / undefined 可赋值性TS2322
strictFunctionTypes函数参数逆变TS2322
strictBindCallApplybind / call / apply 参数TS2345
strictPropertyInitialization类属性必须初始化TS2564
noImplicitThis隐式 any 的 thisTS2683
useUnknownInCatchVariablescatch 变量为 unknownTS2322
alwaysStrict输出 "use strict"—
strictBuiltinIteratorReturn内建迭代器返回类型更严格见 16

⚠️ 不在 strict 里但强烈建议开启的两个(这是最常被误传的一点):

选项为什么不在 strict 里建议
noUncheckedIndexedAccess会让大量现有代码报错✅ 建议开 🔥
exactOptionalPropertyTypes语义变化较大✅ 新项目建议开
noImplicitOverride同上✅ 建议开
noUnusedLocals / noUnusedParameters属风格问题⚠️ 交给 lint 更好
noImplicitReturns同上⚠️ 按团队口味
noFallthroughCasesInSwitch同上✅ 建议开
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 推荐的严格性配置(strict 之外再补几项)
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,   // 索引访问加 undefined 🔥
    "exactOptionalPropertyTypes": true, // 区分「缺省」与「值为 undefined」
    "noImplicitOverride": true,         // 覆盖基类必须写 override
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true // 副作用导入也检查(6.0 已默认开)
  }
}

🔥 noUncheckedIndexedAccess 的正确理解:它给索引访问的结果加上 | undefined。

1
2
3
4
5
6
7
declare const arr: string[];
const a: string = arr[0];
// ❌ TS2322: Type 'string | undefined' is not assignable to type 'string'.

declare const d: Record<string, number>;
const v: number = d["k"];
// ❌ TS2322: Type 'number | undefined' is not assignable to type 'number'.

⚠️ 重要前提:它依赖 strictNullChecks。如果 strictNullChecks 关闭,这个开关静默失效(因为类型系统里根本没有 undefined 类型可加)。这不是 bug,但会让你误以为开了就安全。

有几个选项不能脱离 strictNullChecks 单独使用,否则报 TS5052:

1
2
3
4
error TS5052: Option 'exactOptionalPropertyTypes' cannot be specified
              without specifying option 'strictNullChecks'.
error TS5052: Option 'strictPropertyInitialization' cannot be specified
              without specifying option 'strictNullChecks'.
选项依赖 strictNullChecks
exactOptionalPropertyTypes✅ 必须
strictPropertyInitialization✅ 必须
noUncheckedIndexedAccess⚠️ 不报错,但静默失效

exactOptionalPropertyTypes 到底改了什么:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Opt = { a?: number };

// 默认(关闭时):a?: number 等价于 number | undefined
const x: Opt = { a: undefined };   // ✅ 允许

// 开启后:a?: number 表示「可以没有 a」,但有了就必须是 number
const y: Opt = { a: undefined };
// ❌ TS2375: Type '{ a: undefined; }' is not assignable to type 'Opt'
//    with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined'
//    to the types of the target's properties.

想同时允许「缺省」和「显式 undefined」:

1
2
type Opt2 = { a?: number | undefined };   // ✅ 显式写出 undefined
const z: Opt2 = { a: undefined };         // ✅
选项作用建议
skipLibCheck跳过 .d.ts 文件的类型检查✅ 几乎总是开 🔥
skipDefaultLibCheck只跳过内置 lib.*.d.ts少见,已被 skipLibCheck 覆盖
noEmitOnError有错就不产物⚠️ 构建流水线里按需
1
{ "compilerOptions": { "skipLibCheck": true } }

⚠️ 关于 skipLibCheck 的正确认识:

常见误解事实
「开了就不检查我的代码」❌ 只跳过 .d.ts,你的 .ts 照样全查
「开了就失去类型安全」⚠️ 只影响依赖包声明文件之间的冲突检查
「不该开」❌ 大型项目不开会慢几倍,且常被第三方 .d.ts 之间的冲突阻塞

它跳过的是「不同版本的 @types 互相不兼容」这类你无法修复的问题。建议开,但要知道它的边界:你自己的 .d.ts 也不查了。所以手写声明要额外仔细。


模块与解析(Modules)

选项作用6.0 状态
module产物模块格式amd/umd/systemjs/none 🗑️ 废弃
moduleResolution解析策略node/node10/classic 🗑️ 废弃
rootDir源码根目录⚠️ 不再推断
outDir产物目录—
paths路径别名✅ baseUrl 废弃后前缀写这里
baseUrl路径基准🗑️ 废弃(TS5101)
types加载哪些 @types⚠️ 默认 []
typeRoots@types 搜索根—
resolveJsonModule允许导入 .json—
allowImportingTsExtensions允许 import "./x.ts"需配 noEmit 或用 rewriteRelativeImportExtensions
rewriteRelativeImportExtensions编译时把 .ts 改写成 .js🆕 5.7+
noUncheckedSideEffectImports检查 import "./x" 是否存在6.0 默认 开
resolvePackageJsonExports遵循 exports 字段bundler/nodenext 下默认开
customConditions自定义 exports 条件发库/多环境时用
allowUmdGlobalAccess允许 UMD 全局访问少见

module 与 moduleResolution 的合法搭配 🔥:

你写的是modulemoduleResolution扩展名要求
Node.js 库/服务nodenextnodenext⚠️ ESM 下必须写 .js
Vite / Next / 打包器esnextbundler不写扩展名 ✅
交给打包器、TS 只检查preservebundler不写扩展名 ✅
老 CJS 项目commonjsnodenext不写扩展名 ✅

⚠️ nodenext 下写扩展名的规则最反直觉:源文件是 foo.ts,导入要写 "./foo.js"。原因是 import 语句原样保留到产物,运行时需要指向真实 .js 文件。见 07 模块系统。

types 的取舍:

1
2
3
4
5
6
7
8
// 🛑 6.0 下不写 types,@types/node 不会被加载
{}

// ✅ 显式列出需要的全局类型包
{ "compilerOptions": { "types": ["node", "vitest/globals"] } }

// ⚠️ 恢复 5.x 的「全部加载」行为(不推荐,会拖慢编译)
{ "compilerOptions": { "types": ["*"] } }
场景types 怎么写
Node 服务["node"]
Node + Vitest["node", "vitest/globals"]
浏览器 + Jest["jest"]
纯库(无全局依赖)[](默认即可)✅ 最好
不想管["*"](性能代价)

💭 最佳实践是保持 types: [],让每个文件显式 import。全局类型越少,编译越快,依赖关系越清晰。


目标与环境(Language and Environment)

选项作用说明
target产物 JS 版本6.0 默认 ES2025;最低 ES2015
lib可用的内置类型库不写则按 target 推导
jsxJSX 转换方式6.0 默认 preserve
jsxImportSource新 JSX 运行时的导入源react-jsx 时用,如 "react"
jsxFactory经典 JSX 工厂函数React.createElement
jsxFragmentFactoryFragment 工厂React.Fragment
libReplacement允许用自定义 lib 替换内置6.0 默认 false
noLib不加载任何内置 lib极端场景
moduleDetection如何判断文件是模块force 可避免「意外全局」
useDefineForClassFields类字段用 define 语义target ≥ ES2022 时默认 true
experimentalDecorators旧版装饰器⚠️ 与标准装饰器二选一
emitDecoratorMetadata生成装饰器元数据依赖 experimentalDecorators

lib 对照表:

target自动包含的 lib
ES2025(6.0 默认)lib.es2025.full.d.ts(含 DOM)
ES2022lib.es2022.full.d.ts
ES2020lib.es2020.full.d.ts
ES2015lib.es2015.full.d.ts
1
2
3
4
5
6
7
8
// 手动指定 lib:想用 ES2023 的数组方法但要 DOM
{ "compilerOptions": { "target": "ES2020", "lib": ["ES2023", "DOM", "DOM.Iterable"] } }

// 后端项目:不要 DOM
{ "compilerOptions": { "lib": ["ES2023"] } }

// 前端项目:要 DOM + DOM.Iterable
{ "compilerOptions": { "lib": ["ES2023", "DOM", "DOM.Iterable"] } }

⚠️ lib 决定「有哪些全局类型可用」,target 决定「产物语法」,两者独立。常见错误是只改 target 却忘了 lib,导致新 API 的类型找不到(或反过来,类型有了但运行时环境不支持)。

💡 DOM.Iterable 让 NodeList、HTMLCollection 等支持 for...of 与展开。前端项目建议总是带上。

关于装饰器:TS 5.0 起支持标准装饰器(无需开关)。experimentalDecorators 是旧的实验版,两者语义不同,不能混用:

1
2
3
4
5
// 标准装饰器(新项目推荐)
{ "compilerOptions": {} }

// 旧版装饰器(Angular、TypeORM 等依赖元数据的框架需要)
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }

产物(Emit)

选项作用
noEmit只检查不产物 🔥
outDir产物目录
declaration生成 .d.ts
declarationMap生成 .d.ts.map(能跳回源码)
declarationDir.d.ts 单独目录
emitDeclarationOnly只产 .d.ts
sourceMap生成 .js.map
inlineSourceMap内联 sourcemap
inlineSources把源码内联进 map
removeComments去掉注释
noEmitOnError有错不产物
importHelpers从 tslib 导入辅助函数
noEmitHelpers不生成辅助函数
downlevelIteration🗑️ 废弃
outFile🗑️ 废弃,用打包器
preserveConstEnums保留 const enum 的运行时对象
stripInternal删除标了 @internal 的声明
newLine换行符 crlf / lf
emitBOM输出 BOM
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 库的典型产物配置
{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,   // 使用者能跳回你的源码 🔥
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

⚠️ stripInternal 配合 /** @internal */ 注释,可以把内部 API 从 .d.ts 里删掉。但它只删声明,不删实现——运行时那些 API 仍然存在,只是使用者没有类型。详见 13 发布库。


互操作约束(Interop Constraints)

选项作用6.0 状态
isolatedModules保证可单文件转译✅ 建议开
verbatimModuleSyntax类型必须用 import type✅ 建议开 🔥
erasableSyntaxOnly禁止非擦除语法🆕 5.8+,Node 直跑 TS 时开
isolatedDeclarations要求显式返回类型,便于并行生成声明🆕 5.5+,需 declaration 或 composite
esModuleInteropCJS/ESM 互操作⚠️ 恒为 true,写 false 报 TS5107
allowSyntheticDefaultImports允许合成默认导入⚠️ 恒为 true
forceConsistentCasingInFileNames大小写一致性✅ 保持默认 true
preserveSymlinks保留符号链接少见
1
2
3
4
5
6
7
8
9
// 现代项目的互操作配置
{
  "compilerOptions": {
    "isolatedModules": true,
    "verbatimModuleSyntax": true,
    "erasableSyntaxOnly": true,       // 只在需要 Node 直跑时开
    "forceConsistentCasingInFileNames": true
  }
}

erasableSyntaxOnly 会禁止这些语法(实测报 TS1294):

被禁语法替代
enum用 as const 对象 + 联合类型 🔥
带运行时代码的 namespace用 ES 模块
构造函数参数属性 constructor(public a)手写字段 + 赋值
import x = require()用 import
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 🛑 erasableSyntaxOnly 下报 TS1294
export enum E { A }
export namespace N { export const x = 1; }
export class K { constructor(public a: number) {} }

// ✅ 替代写法
export const E = { A: "A" } as const;
export type E = (typeof E)[keyof typeof E];

export const N = { x: 1 };

export class K {
  a: number;
  constructor(a: number) { this.a = a; }
}

💭 打开 erasableSyntaxOnly 的一个附带好处:它强迫你放弃 enum,而 enum 本来就有不少坑(见 16)。用 as const 对象替代更符合 JS 习惯,且能被 Node 直接运行。


项目引用与增量(Projects)

选项作用
composite声明为可被引用的项目,强制 declaration
incremental增量编译,生成 .tsbuildinfo
tsBuildInfoFile指定 .tsbuildinfo 位置
references引用其它项目(数组,含 path)
disableSourceOfProjectReferenceRedirect禁用源码重定向
disableReferencedProjectLoad不自动加载被引用项目
disableSolutionSearching减少 solution 搜索
1
2
3
4
5
6
7
8
// 根 tsconfig.json(solution 文件,只有引用不含源码)
{
  "files": [],
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/web" }
  ]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// packages/core/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "rootDir": "./src",
    "outDir": "./dist"
  },
  "include": ["src"]
}
1
2
3
npx tsc --build          # 按拓扑顺序增量构建
npx tsc --build --clean  # 清理
npx tsc --build --force  # 强制全量

💡 项目引用适合 monorepo:它让每个包独立检查、增量复用,大仓库能快很多。代价是配置变复杂,且必须开 composite + declaration。详见 17 编译器与性能。


JavaScript 支持与迁移

选项作用
allowJs允许编译 .js
checkJs检查 .js(配合 // @ts-check)
maxNodeModuleJsDepth检查 node_modules 里 JS 的深度
1
2
3
4
5
6
7
8
// 迁移期配置
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true
  }
}

详见 15 从 JS 迁移。


配置继承:extends

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// tsconfig.base.json(团队共享)
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "target": "ES2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "skipLibCheck": true,
    "verbatimModuleSyntax": true
  }
}
1
2
3
4
5
6
// 各包继承
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": { "outDir": "./dist" },
  "include": ["src"]
}
规则说明
相对路径相对配置文件自身解析
compilerOptions深合并,后者覆盖前者
files / include / exclude整体覆盖,不合并 ⚠️
references覆盖
数组型选项覆盖(如 lib、types)
多重继承TS 5.0+ 支持 extends: ["./a.json", "./b.json"] 🆕

⚠️ 数组选项是覆盖不是合并:父配置写了 "lib": ["ES2023", "DOM"],子配置写 "lib": ["ES2023"],结果是只剩 ES2023,DOM 类型全丢。

从 npm 包继承(团队共享配置的标准做法):

1
2
{ "extends": "@tsconfig/strictest/tsconfig.json" }
{ "extends": "@tsconfig/node22/tsconfig.json" }

查看最终生效配置(排查继承问题必用)🔥:

1
npx tsc --showConfig

可直接抄的配置模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
  "compilerOptions": {
    // 环境与目标
    "target": "ES2023",
    "lib": ["ES2023"],
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "types": ["node"],

    // 严格性
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "noFallthroughCasesInSwitch": true,

    // 互操作
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,

    // 产物
    "rootDir": "./src",
    "outDir": "./dist",
    "sourceMap": true,
    "declaration": false,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

注意 nodenext 下导入要写 .js 扩展名:

1
import { helper } from "./helper.js";   // ✅ 源文件是 helper.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2023", "DOM", "DOM.Iterable"],
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "types": ["vite/client"],

    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noFallthroughCasesInSwitch": true,

    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "forceConsistentCasingInFileNames": true,

    "noEmit": true,          // Vite 负责产物,tsc 只做检查 🔥
    "skipLibCheck": true,

    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src"]
}

⚠️ paths 只影响类型检查,运行时别名要在 vite.config.ts 里同步配置 resolve.alias。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2023"],
    "module": "nodenext",
    "moduleResolution": "nodenext",

    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "isolatedDeclarations": true,   // 让声明生成可以并行,加快构建 🆕

    // 产物:声明与 JS 都要
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "rootDir": "./src",
    "outDir": "./dist",

    "skipLibCheck": true
  },
  "include": ["src"]
}

isolatedDeclarations 要求所有导出都有显式返回类型(否则报错),换来的是声明文件生成可以脱离类型推断、支持并行。库项目值得开。见 13 发布库。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noUnusedLocals": false,       // CI 里交给 lint
    "noEmit": true,
    "skipLibCheck": true,
    "types": ["node"]
  },
  "include": ["src", "tests"]
}
1
npx tsc --noEmit

🔥 无论用什么打包器,CI 里都应该有这一步——打包器不做类型检查。


诊断类选项(放在 tsconfig 里)

这些也可以写进 tsconfig.json,省得每次敲命令行:

1
2
3
4
5
{
  "compilerOptions": {
    "noErrorTruncation": true   // 不截断超长类型错误,排查时必开 🔥
  }
}
选项作用
noErrorTruncation完整显示类型,不截断
pretty彩色错误输出
listFiles列出参与编译的文件
explainFiles解释每个文件为何被包含
traceResolution追踪模块解析
extendedDiagnostics各阶段耗时
diagnostics编译统计
generateTrace生成性能剖析
noCheck完全不检查,只产物(应急用)

常见陷阱小结

陷阱症状正确做法
升级 6.0 后找不到 processTS2304types: ["node"]
产物多了一层 src/路径不对显式 rootDir
用 baseUrlTS5101前缀写进 paths
用 moduleResolution: "node"TS5107"bundler" 或 "nodenext"
nodenext 下漏扩展名TS2835写 "./x.js"
以为 strict 含全部检查索引访问仍不安全另开 noUncheckedIndexedAccess
noUncheckedIndexedAccess 无效果静默失效检查 strictNullChecks 是否开
extends 后 lib 丢了类型缺失数组选项是覆盖不是合并
以为 paths 影响运行时运行时找不到模块同步配打包器别名
skipLibCheck 当成万能自己的 .d.ts 也没检查手写声明要额外仔细
isolatedModules 下 const enum报错用 as const 对象
混用两种装饰器行为怪异只启用一种
最后修改 September 20, 2026: 更新 (25684a4ed)