tsconfig.json 完整配置指南
一句话概述:每个选项的作用、2026 推荐配置模板、项目类型对应最佳实践
tsconfig.json 是什么?
tsconfig.json 是 TypeScript 项目的配置文件,定义了编译选项、文件包含范围和项目引用关系。放在项目根目录下,tsc 命令会自动读取它。
一、2026 推荐基础配置
通用推荐(适用于大多数项目)
{
"compilerOptions": {
// ===== 类型检查 =====
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// ===== 模块 =====
"module": "ESNext",
"moduleResolution": "bundler",
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
// ===== 输出 =====
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
// ===== 编译优化 =====
"skipLibCheck": true,
"incremental": true,
// ===== 代码质量 =====
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
// ===== 互操作 =====
"esModuleInterop": true,
"isolatedModules": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}二、类型检查选项详解
strict(必须开启)
strict: true 等价于同时开启以下所有子选项:
strict = true 包含:
├── strictNullChecks → null/undefined 不能赋给其他类型
├── strictFunctionTypes → 函数参数严格逆变检查
├── strictBindCallApply → bind/call/apply 参数严格检查
├── strictPropertyInitialization → 类属性必须初始化
├── noImplicitAny → 禁止隐式 any
├── noImplicitThis → 禁止隐式 this
├── alwaysStrict → 输出文件添加 "use strict"
└── useUnknownInCatchVariables → catch 的 e 类型为 unknown每个子选项的作用:
// strictNullChecks
let name: string;
// name = null; // ❌ 不能将 null 赋给 string
// name = undefined; // ❌
// strictFunctionTypes
type Handler = (e: Event) => void;
const mouseHandler = (e: MouseEvent) => {};
// const h: Handler = mouseHandler; // ❌ 参数逆变
// noImplicitAny
function fn(x) {} // ❌ 参数 x 隐式 any
function fn(x: any) {} // ✅ 显式 any(但不推荐)
// useUnknownInCatchVariables
try { /* ... */ } catch (e) {
// e: unknown(必须收窄后使用)
if (e instanceof Error) {
console.log(e.message); // ✅
}
}noUncheckedIndexedAccess(强烈推荐)
索引访问的返回类型自动加上 | undefined:
// 开启前
const arr: string[] = ['a', 'b'];
const item = arr[10]; // string(实际是 undefined!)
// 开启后
const item = arr[10]; // string | undefined ✅ 更安全
if (item !== undefined) {
item.toUpperCase(); // 收窄后使用
}
// 对象索引签名同理
const map: Record<string, number> = {};
const val = map['key']; // number | undefinedexactOptionalPropertyTypes(推荐)
区分"属性不存在"和"属性值为 undefined":
interface User {
name: string;
bio?: string; // 可选属性
}
// 开启前:bio 可以赋值为 undefined
const user1: User = { name: 'Alice', bio: undefined }; // ✅
// 开启后:bio 只能不写或赋 string
const user2: User = { name: 'Alice', bio: undefined }; // ❌
const user3: User = { name: 'Alice' }; // ✅
const user4: User = { name: 'Alice', bio: 'hello' }; // ✅三、模块与解析选项详解
module
指定输出的模块格式:
| 值 | 说明 | 推荐场景 |
|---|---|---|
ESNext | 保持最新 ES Module 语法 | 打包器项目(Vite、Webpack) |
NodeNext | Node.js 原生 ESM + CJS 双模式 | Node.js 项目 |
CommonJS | 输出 require/exports | 旧版 Node.js |
ES2022 | ES Module(固定版本) | 指定最低支持版本 |
moduleResolution
控制 import 语句如何找到模块文件:
moduleResolution 选项:
├── "bundler" → 2026 推荐,适用于 Vite/Webpack/esbuild
│ 支持 exports 字段,不强制扩展名
├── "node16" → Node.js 16+ ESM 模式
│ "nodenext" 需要 .js 扩展名,支持 exports 字段
├── "node" → Node.js 经典 CJS 模式(旧版)
│ 按 node_modules 查找
└── "classic" → TypeScript 早期模式(已过时)// Vite / Webpack 项目
{
"moduleResolution": "bundler",
"module": "ESNext"
}
// Node.js 纯后端项目
{
"moduleResolution": "nodenext",
"module": "NodeNext"
}verbatimModuleSyntax(TS 5.0+,推荐)
取代 isolatedModules,强制类型导入使用 import type:
// ✅ 正确
import type { User } from './types';
import { createUser } from './utils';
// 混合导入也可以
import { createUser, type User } from './user';
// ❌ 错误:类型导入必须用 import type
import { User } from './types'; // 如果 User 只是类型为什么推荐:打包器(esbuild、SWC)只做语法转换不做类型分析,import type 让它们知道哪些导入可以安全移除。
paths(路径别名)
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}注意:
paths只影响 TypeScript 的类型解析,不影响运行时。需要配合打包器(Vite 的resolve.alias、Webpack 的resolve.alias)或tsconfig-paths包。
四、输出选项详解
target
控制输出的 JavaScript 版本:
| 值 | 关键特性 | 推荐场景 |
|---|---|---|
ES2022 | Top-level await、class fields、#private | 现代浏览器 / Node 18+ |
ES2020 | BigInt、??、?. | 兼容较旧环境 |
ESNext | 最新标准 | 输出交给打包器处理 |
ES5 | 无箭头函数、无 class | 极老浏览器(不推荐) |
lib
声明项目可用的全局 API 类型:
{
"lib": [
"ES2022", // ES2022 标准库(Promise、Map 等)
"DOM", // 浏览器 DOM API
"DOM.Iterable", // DOM 集合的迭代器
"WebWorker" // Web Worker API(如需要)
]
}declaration 和 declarationMap
{
"declaration": true, // 生成 .d.ts 类型声明文件
"declarationMap": true, // 生成 .d.ts.map,IDE 可跳转到源码
"emitDeclarationOnly": true // 只生成 .d.ts,不生成 .js(库开发常用)
}sourceMap
{
"sourceMap": true, // 生成 .js.map
"inlineSourceMap": false, // 不内联到 .js 中
"inlineSources": false // 不将源码嵌入 sourcemap
}五、编译优化选项
skipLibCheck(推荐开启)
跳过 node_modules 中 .d.ts 文件的类型检查:
- 优点:大幅加速编译(某些项目提升 50%+)
- 缺点:可能漏掉第三方库的类型错误
- 实践:几乎所有项目都应该开启,第三方库的类型问题不是你的责任
incremental
{
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
}开启后 TypeScript 会生成 .tsbuildinfo 文件记录编译状态,下次编译只处理变更的文件。
项目引用(Project References)
// tsconfig.json(根项目)
{
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" },
{ "path": "./packages/utils" }
],
"files": [] // 根项目不直接编译文件
}
// packages/core/tsconfig.json
{
"compilerOptions": {
"composite": true, // 子项目必须开启
"declaration": true, // composite 要求开启
"outDir": "./dist"
},
"references": [
{ "path": "../utils" } // 声明依赖
]
}使用 tsc --build(tsc -b)编译,自动处理依赖顺序和增量编译。
六、项目类型配置模板
React + Vite 项目
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"jsx": "react-jsx",
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src", "vite-env.d.ts"]
}Next.js 项目
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"jsx": "preserve",
"plugins": [{ "name": "next" }],
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"incremental": true,
"noEmit": true,
"esModuleInterop": true,
"allowJs": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}Node.js 后端项目
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "nodenext",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}npm 库开发
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"verbatimModuleSyntax": true,
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "./dist",
"rootDir": "./src",
"composite": true,
"skipLibCheck": true
},
"include": ["src"]
}七、常见问题
isolatedModules vs verbatimModuleSyntax
isolatedModules:TS 4.x 时代的方案,确保每个文件可以独立编译verbatimModuleSyntax:TS 5.0+ 的替代方案,更严格也更清晰- 推荐:新项目用
verbatimModuleSyntax,同时可以保留isolatedModules兼容旧工具
jsx 选项
| 值 | 输出 | 场景 |
|---|---|---|
react-jsx | 使用 _jsx 自动导入(React 17+) | React + Vite/Webpack |
preserve | 保留 JSX 不转换 | Next.js(由框架处理) |
react | React.createElement(旧版) | React 16 及以下 |
react-jsxdev | 开发模式带调试信息 | 开发环境 |
noEmit vs emitDeclarationOnly
noEmit: true:不输出任何文件,只做类型检查(前端项目常用,JS 输出交给 Vite/Webpack)emitDeclarationOnly: true:只输出.d.ts,不输出.js(库开发常用,JS 输出交给 SWC/esbuild)
面试高频题
1. strict: true 包含哪些子选项?为什么必须开启?
答案:包含 strictNullChecks、strictFunctionTypes、strictBindCallApply、strictPropertyInitialization、noImplicitAny、noImplicitThis、alwaysStrict、useUnknownInCatchVariables 共 8 个子选项。必须开启是因为这些检查能在编译期捕获大量潜在 bug(空值访问、参数类型错误、隐式 any 等),是 TypeScript 类型安全的基础。
2. moduleResolution: "bundler" 和 "node16" 的区别?
答案:bundler 适用于前端打包器环境(Vite/Webpack),支持 package.json 的 exports 字段但不强制文件扩展名。node16/nodenext 适用于 Node.js 原生 ESM,要求导入时写 .js 扩展名,严格遵循 Node.js 的模块解析规则。前端项目用 bundler,纯 Node.js 后端用 nodenext。
3. skipLibCheck 会有什么风险?
答案:跳过第三方库 .d.ts 文件的类型检查,可能漏掉第三方库之间的类型冲突或版本不兼容问题。但实际风险很小,因为:(1)主流库的类型定义已经过充分测试;(2)你的项目代码仍然会被完整检查;(3)编译速度提升明显(大型项目可提升 50%+)。几乎所有项目都应该开启。
4. verbatimModuleSyntax 的作用是什么?
答案:强制类型导入使用 import type 语法。这让 esbuild、SWC 等只做语法转换的工具知道哪些导入是纯类型(可安全移除),哪些是运行时需要的值。取代了旧版的 isolatedModules + importsNotUsedAsValues 组合,语义更清晰。