Skip to content

编译选项详解

概述

TypeScript 编译器的 compilerOptions 提供了丰富的配置选项,用于控制编译行为、类型检查严格程度、模块解析策略等。合理配置这些选项可以帮助你:

  • 提高代码质量和类型安全性
  • 优化编译性能
  • 适配不同的运行环境和构建工具
  • 控制输出格式和兼容性

本文档将详细介绍各个编译选项的作用、使用场景和最佳实践。

编译选项分类

编译选项可以大致分为以下几类:

  1. 目标版本和模块系统:控制编译输出的 JavaScript 版本和模块格式
  2. 类型检查选项:控制类型检查的严格程度
  3. 模块解析选项:控制模块的查找和解析方式
  4. 输出选项:控制编译输出的格式和位置
  5. JSX 选项:控制 JSX 的编译方式
  6. 其他选项:性能优化、调试支持等

目标版本和模块系统

target

指定编译后的 JavaScript 目标版本。

json
{
  "compilerOptions": {
    "target": "ES2020"
  }
}

可选值

  • ES3ES5:兼容旧浏览器
  • ES2015(ES6)、ES2016ES2017ES2018ES2019ES2020ES2021ES2022:对应年份的 ECMAScript 标准
  • ESNext:最新的 ECMAScript 特性

示例

typescript
// 源代码
async function fetchData() {
  const response = await fetch('/api/data');
  return response.json();
}

// target: "ES5" 编译后(需要 polyfill)
// 会转换为 Promise 链式调用

// target: "ES2017" 编译后
// 保留 async/await 语法

提示

选择 target 时,需要考虑目标运行环境。如果使用现代打包工具(如 webpack、vite),通常选择较新的版本(如 ES2020),由打包工具处理兼容性。

module

指定模块系统。

json
{
  "compilerOptions": {
    "module": "commonjs"
  }
}

可选值

  • none:不使用模块系统
  • commonjs:Node.js 使用的 CommonJS
  • amd:AMD 模块(RequireJS)
  • system:SystemJS 模块
  • umd:UMD 模块(通用模块定义)
  • es6 / es2015 / es2020 / esnext:ES 模块
  • node16 / nodenext:Node.js 16+ 的模块系统

示例

typescript
// 源代码
export function greet(name: string) {
  return `Hello, ${name}!`;
}

// module: "commonjs" 编译后
exports.greet = function(name) {
  return "Hello, " + name + "!";
};

// module: "es2020" 编译后
export function greet(name) {
  return `Hello, ${name}!`;
}

lib

指定包含的类型定义库。

json
{
  "compilerOptions": {
    "lib": ["ES2020", "DOM", "DOM.Iterable"]
  }
}

常用值

  • ES5ES2015ES2020 等:ECMAScript 标准库
  • DOM:浏览器 DOM API
  • DOM.Iterable:DOM 迭代器(如 NodeList)
  • WebWorker:Web Worker API
  • ScriptHost:Windows Script Host

示例

typescript
// lib: ["ES5"] - 只能使用 ES5 的 API
const arr = [1, 2, 3];
arr.forEach(item => console.log(item)); // ✅ 可用

// lib: ["ES2020"] - 可以使用 ES2020 的 API
const arr = [1, 2, 3];
const doubled = arr.flatMap(x => [x, x * 2]); // ✅ 可用

// lib: ["DOM"] - 可以使用浏览器 API
const element = document.getElementById('app'); // ✅ 可用

注意

lib 选项只影响类型检查,不影响运行时。即使没有包含 DOM,代码仍然可以在浏览器中运行,只是 TypeScript 不会识别 DOM API 的类型。

类型检查选项

strict

启用所有严格类型检查选项的快捷方式。

json
{
  "compilerOptions": {
    "strict": true
  }
}

启用 strict: true 会自动启用以下选项:

  • noImplicitAny
  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitThis
  • alwaysStrict

最佳实践

建议所有新项目都启用 strict: true,可以在开发早期发现潜在的类型问题。

noImplicitAny

不允许隐式的 any 类型。

json
{
  "compilerOptions": {
    "noImplicitAny": true
  }
}

示例

typescript
// ❌ 错误:参数 'x' 隐式具有 'any' 类型
function add(x, y) {
  return x + y;
}

// ✅ 正确:明确指定类型
function add(x: number, y: number): number {
  return x + y;
}

strictNullChecks

严格检查 nullundefined

json
{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

示例

typescript
// strictNullChecks: false(默认)
let name: string;
name = null; // ✅ 允许
name = undefined; // ✅ 允许

// strictNullChecks: true
let name: string;
name = null; // ❌ 错误:不能将类型 "null" 分配给类型 "string"
name = undefined; // ❌ 错误:不能将类型 "undefined" 分配给类型 "string"

// ✅ 正确:使用联合类型
let name: string | null | undefined;
name = null; // ✅ 允许
name = undefined; // ✅ 允许

实际应用

typescript
// 函数可能返回 null
function findUser(id: number): User | null {
  const user = users.find(u => u.id === id);
  return user || null;
}

// 必须检查 null
const user = findUser(1);
if (user !== null) {
  console.log(user.name); // ✅ 类型安全
} else {
  console.log('User not found');
}

strictFunctionTypes

严格检查函数类型。

json
{
  "compilerOptions": {
    "strictFunctionTypes": true
  }
}

示例

typescript
// 基础类型
type Handler = (event: Event) => void;

// 子类型
type MouseHandler = (event: MouseEvent) => void;

// strictFunctionTypes: false(默认)
let handler: Handler;
handler = (event: MouseEvent) => {}; // ✅ 允许(不严格)

// strictFunctionTypes: true
let handler: Handler;
handler = (event: MouseEvent) => {}; // ❌ 错误:类型不兼容

strictPropertyInitialization

严格检查类属性初始化。

json
{
  "compilerOptions": {
    "strictPropertyInitialization": true
  }
}

示例

typescript
// strictPropertyInitialization: true
class User {
  name: string; // ❌ 错误:属性 'name' 没有初始化
  age: number; // ❌ 错误:属性 'age' 没有初始化
}

// ✅ 解决方案 1:在构造函数中初始化
class User {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// ✅ 解决方案 2:使用明确赋值断言
class User {
  name!: string; // 告诉 TypeScript 我们会在其他地方初始化
  age!: number;
}

// ✅ 解决方案 3:使用默认值
class User {
  name: string = '';
  age: number = 0;
}

noUnusedLocals / noUnusedParameters

不允许未使用的局部变量和参数。

json
{
  "compilerOptions": {
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

示例

typescript
// ❌ 错误:未使用的局部变量
function process() {
  const unused = 42; // 错误
  return true;
}

// ✅ 正确:移除未使用的变量
function process() {
  return true;
}

// ❌ 错误:未使用的参数
function handler(event: Event, unused: string) { // 'unused' 未使用
  console.log(event.type);
}

// ✅ 解决方案 1:移除参数
function handler(event: Event) {
  console.log(event.type);
}

// ✅ 解决方案 2:使用下划线前缀
function handler(event: Event, _unused: string) {
  console.log(event.type);
}

noImplicitReturns

函数必须显式返回值。

json
{
  "compilerOptions": {
    "noImplicitReturns": true
  }
}

示例

typescript
// ❌ 错误:函数并非所有代码路径都返回值
function getValue(x: number): number {
  if (x > 0) {
    return x;
  }
  // 缺少 else 分支的返回值
}

// ✅ 正确:所有路径都有返回值
function getValue(x: number): number {
  if (x > 0) {
    return x;
  }
  return 0;
}

模块解析选项

moduleResolution

指定模块解析策略。

json
{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

可选值

  • node:Node.js 风格的模块解析(推荐)
  • classic:TypeScript 1.6 之前的解析方式(已弃用)
  • bundler:适用于现代打包工具(webpack、vite 等)
  • node16 / nodenext:Node.js 16+ 的模块解析

示例

typescript
// moduleResolution: "node"
import { utils } from './utils'; // 会查找 ./utils.ts, ./utils.tsx, ./utils.d.ts, ./utils/index.ts

// moduleResolution: "bundler"
import { utils } from './utils'; // 允许更灵活的解析,适合打包工具

baseUrl

设置基础路径,用于解析非相对模块导入。

json
{
  "compilerOptions": {
    "baseUrl": "./src"
  }
}

示例

typescript
// baseUrl: "./src"
import { User } from 'models/user'; // 解析为 ./src/models/user
import { utils } from 'utils/helpers'; // 解析为 ./src/utils/helpers

paths

路径映射,将模块路径映射到实际路径。

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "components/*": ["src/components/*"],
      "utils/*": ["src/utils/*"]
    }
  }
}

示例

typescript
// 使用路径映射
import { Button } from '@/components/Button';
import { formatDate } from 'utils/date';
import { User } from 'models/User';

// 实际解析为:
// @/components/Button -> src/components/Button
// utils/date -> src/utils/date
// models/User -> src/models/User

注意

paths 只影响 TypeScript 的类型检查,不影响运行时模块解析。如果使用路径映射,需要在打包工具(webpack、vite 等)中配置相应的别名。

typeRoots / types

指定类型定义的查找位置。

json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./types"],
    "types": ["node", "jest"]
  }
}

说明

  • typeRoots:指定查找类型定义的根目录
  • types:明确指定要包含的类型包(如果指定,只包含列出的包)

示例

typescript
// typeRoots: ["./node_modules/@types"]
// 会自动包含 @types/node, @types/jest 等

// types: ["node", "jest"]
// 只包含 @types/node 和 @types/jest,忽略其他 @types 包

esModuleInterop

启用 ES 模块与 CommonJS 的互操作性。

json
{
  "compilerOptions": {
    "esModuleInterop": true
  }
}

示例

typescript
// esModuleInterop: false
import * as express from 'express'; // 必须使用命名空间导入
const app = express();

// esModuleInterop: true
import express from 'express'; // ✅ 可以使用默认导入
const app = express();

allowSyntheticDefaultImports

允许从没有默认导出的模块进行默认导入。

json
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true
  }
}

提示

esModuleInterop: true 时,allowSyntheticDefaultImports 会自动启用。

resolveJsonModule

允许导入 JSON 模块。

json
{
  "compilerOptions": {
    "resolveJsonModule": true
  }
}

示例

typescript
// resolveJsonModule: true
import config from './config.json';

console.log(config.appName);
console.log(config.version);

输出选项

outDir

指定编译输出的目录。

json
{
  "compilerOptions": {
    "outDir": "./dist"
  }
}

rootDir

指定源代码的根目录。

json
{
  "compilerOptions": {
    "rootDir": "./src"
  }
}

示例

项目结构:
src/
  app.ts
  utils/
    helper.ts

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

输出结构:
dist/
  app.js
  utils/
    helper.js

noEmit

只进行类型检查,不生成 JavaScript 文件。

json
{
  "compilerOptions": {
    "noEmit": true
  }
}

使用场景

适用于使用打包工具(webpack、vite)的项目,TypeScript 只负责类型检查,编译由打包工具处理。

declaration

生成 .d.ts 声明文件。

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

示例

typescript
// src/utils.ts
export function formatDate(date: Date): string {
  return date.toISOString();
}

// 生成 dist/utils.js 和 types/utils.d.ts
// types/utils.d.ts:
export declare function formatDate(date: Date): string;

sourceMap

生成源映射文件(.map),用于调试。

json
{
  "compilerOptions": {
    "sourceMap": true
  }
}

JSX 选项

jsx

指定 JSX 的编译方式。

json
{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

可选值

  • preserve:保留 JSX,由其他工具处理(如 Babel)
  • react:编译为 React.createElement 调用
  • react-jsx:使用新的 JSX 转换(React 17+,不需要导入 React)
  • react-jsxdev:开发模式的 JSX 转换
  • react-native:保留 JSX,但生成 .js 文件

示例

tsx
// jsx: "react"
import React from 'react';
function App() {
  return <div>Hello</div>;
}
// 编译为:React.createElement('div', null, 'Hello')

// jsx: "react-jsx"
function App() {
  return <div>Hello</div>;
}
// 编译为:_jsx('div', { children: 'Hello' })
// 不需要导入 React

其他重要选项

allowJs

允许编译 JavaScript 文件。

json
{
  "compilerOptions": {
    "allowJs": true
  }
}

checkJs

对 JavaScript 文件进行类型检查。

json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true
  }
}

skipLibCheck

跳过类型声明文件的类型检查,加快编译速度。

json
{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

性能优化

对于大型项目,启用 skipLibCheck: true 可以显著提高编译速度,因为不需要检查 node_modules 中的类型定义。

forceConsistentCasingInFileNames

强制文件名大小写一致。

json
{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}

示例

typescript
// 文件:User.ts
export class User {}

// 导入时大小写不一致
import { User } from './user'; // ❌ 错误:文件大小写不匹配
import { User } from './User'; // ✅ 正确

isolatedModules

确保每个文件可以安全地作为独立模块使用。

json
{
  "compilerOptions": {
    "isolatedModules": true
  }
}

使用场景

当使用打包工具(如 esbuild、swc)时,启用此选项可以确保代码可以被这些工具正确处理。

incremental

启用增量编译。

json
{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo"
  }
}

说明

  • 只重新编译更改的文件,提高编译速度
  • tsBuildInfoFile 指定存储增量编译信息的文件位置

composite

启用项目引用功能。

json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true
  }
}

说明

  • 用于 TypeScript 项目引用(Project References)
  • 必须同时启用 declaration: true

实际配置示例

示例 1:Node.js 后端项目

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true,
    "incremental": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

示例 2:React 前端项目

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

示例 3:库项目

json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "lib": ["ES2018"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

选项优先级和继承

选项优先级

  1. 命令行参数(最高优先级)
  2. tsconfig.json 中的配置
  3. 继承的配置文件(extends
  4. 默认值(最低优先级)

配置继承示例

json
// base.json
{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

// tsconfig.json
{
  "extends": "./base.json",
  "compilerOptions": {
    "target": "ES2020",
    "outDir": "./dist"
  }
}

// 最终配置:
// - strict: true(继承)
// - esModuleInterop: true(继承)
// - skipLibCheck: true(继承)
// - target: "ES2020"(覆盖)
// - outDir: "./dist"(覆盖)

注意事项

重要提示

  1. 模块系统匹配modulemoduleResolution 需要匹配。例如,module: "esnext" 通常配合 moduleResolution: "bundler" 使用。

  2. 路径映射限制paths 只影响类型检查,运行时需要使用打包工具配置相应的别名。

  3. 严格模式建议:新项目建议启用 strict: true,虽然会增加一些类型注解的工作,但能显著提高代码质量。

  4. 性能优化:对于大型项目,启用 incremental: trueskipLibCheck: true 可以显著提高编译速度。

  5. 输出目录:确保 outDirrootDir 不会重叠,避免编译输出覆盖源代码。

  6. JSX 配置:使用 React 17+ 时,推荐使用 jsx: "react-jsx",不需要在每个文件中导入 React。

最佳实践

  1. 配置文件版本控制:将 tsconfig.json 提交到版本控制系统,确保团队使用相同的配置。

  2. 使用 extends:创建基础配置文件,各子项目继承基础配置,避免重复。

  3. 明确包含/排除:使用 includeexclude 明确指定要编译的文件。

  4. 文档化复杂配置:对于复杂的配置,添加注释或创建配置说明文档。

  5. 定期更新:关注 TypeScript 版本更新,及时采用新的编译选项和最佳实践。

相关链接

基于 VitePress 构建