TS工程配置实践实践
# 08.TS工程配置实践
从 tsconfig.json 到生产构建——掌握 TS 项目工程化的完整链路:编译选项、strict 模式、声明文件、Webpack/Vite 集成。
# 1. 案例引入
# 1.1 一个典型的 TS 项目启动问题
# 你克隆了公司的前端项目,运行:
npm install
npm run dev
# 然后看到:
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
error TS2588: Cannot find name 'Promise'. Do you need to change your target library?
# 你打开 tsconfig.json——200 行配置,20+ 个选项
# 每个都影响编译行为,但文档分散在各处
# 本文就是你的 tsconfig 完全指南
# 2. tsconfig.json 核心选项
# 2.1 项目入口:files / include / exclude
{
"files": [
"src/index.ts"
],
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts"
]
}
files:精确指定编译文件(一般只用于简单场景)include:glob 模式,默认包含 index.ts 所在目录及子目录exclude:排除文件,node_modules默认已排除
# 2.2 extends:继承配置
// tsconfig.json
{
"extends": "@company/tsconfig-base/tsconfig.json",
"compilerOptions": {
"outDir": "./dist"
}
}
// 团队通用配置方案:
// tsconfig.base.json ← 共享的 compilerOptions
// tsconfig.app.json ← extends base + app 特有选项
// tsconfig.node.json ← extends base + Node 特有选项
# 2.3 编译目标与模块系统
{
"compilerOptions": {
// 编译目标 JS 版本——影响语法降级和 lib 默认值
"target": "ES2020",
// "ES3" // 传统浏览器
// "ES2015" // IE11+ 支持
// "ES2020" // 现代项目(可选链 & 空值合并)
// "ESNext" // 最新语法(依赖构建工具降级)
// 模块系统——影响 import/export 编译方式
"module": "ESNext",
// "CommonJS" // Node.js 传统
// "ESNext" // 配合 Vite/Webpack(推荐)
// "ES2020" // 原生 import()
// JSX 处理
"jsx": "react-jsx", // React 17+ 自动引入 jsx
// "preserve" // 保留 JSX 给 Babel 处理
// "react" // 转为 React.createElement
}
}
# 2.4 输出相关
{
"compilerOptions": {
"outDir": "./dist", // 输出目录
"rootDir": "./src", // 源码根目录(保持输出目录结构)
"declaration": true, // 生成 .d.ts 声明文件
"declarationMap": true, // 生成声明文件的 sourcemap
"sourceMap": true, // 生成 .js.map(调试用)
"removeComments": false, // 是否移除注释
"noEmit": true, // 不生成文件——仅做类型检查
}
}
# 3. strict 严格模式逐条拆解
# 3.1 strict 包含的 8 个子项
{
"compilerOptions": {
"strict": true // 等价于以下全部开启
}
}
# 3.2 strictNullChecks
// strictNullChecks: false(危险)
let name: string = null; // ✅
// strictNullChecks: true(安全)
let name: string = null; // ❌
let nameOrNull: string | null = null; // ✅ 显式联合
# 3.3 strictFunctionTypes
// 方法声明 vs 函数属性的逆变区别
interface Handler {
// 方法声明——双向协变(宽松)
onClick(e: MouseEvent): void;
// 函数属性——严格逆变(安全)
onHover: (e: MouseEvent) => void;
}
# 3.4 noImplicitAny / noImplicitReturns / noImplicitThis
// noImplicitAny: true → 禁止隐式 any
function greet(name) { // ❌ name: implicit any
return `Hi, ${name}`;
}
// noImplicitReturns: true → 函数所有路径必须有返回值
function isPositive(n: number) {
if (n > 0) return true;
// ❌ 缺少 else 返回——隐式 undefined
}
// noImplicitThis: true → 禁止隐式 any 的 this
class EventSource {
onLoad() {
// this 必须显式标注或通过箭头函数绑定
}
}
# 3.5 strictPropertyInitialization 和 noUnusedLocals/noUnusedParameters
// strictPropertyInitialization: true → 类属性必须在构造器中初始化
class User {
name: string; // ❌ 未初始化
age: number = 0; // ✅
id!: number; // ✅ 非空断言——"我来负责初始化"
}
// noUnusedLocals/noUnusedParameters: true → 未使用的变量/参数报错
function add(a: number, b: number, _unused: number): number {
// let temp = 0; // ❌ noUnusedLocals
return a + b;
}
# 4. .d.ts 声明文件编写
# 4.1 声明文件的三种角色
.d.ts 文件用于:
1. 为无类型的 JS 库提供类型定义
2. 为项目的公共 API 生成类型声明
3. 全局类型扩充(declare global)
# 4.2 为第三方库编写 .d.ts
// 假设有一个 JS 库 "legacy-utils" 没有类型定义
// types/legacy-utils.d.ts
declare module "legacy-utils" {
// 导出类
export class Calculator {
add(a: number, b: number): number;
multiply(a: number, b: number): number;
}
// 导出函数
export function formatDate(date: Date, format: string): string;
// 导出常量
export const VERSION: string;
// 默认导出
export default Calculator;
}
// 使用
import Calculator from "legacy-utils";
const calc = new Calculator();
calc.add(1, 2);
# 4.3 声明非代码模块
// 让 TS 识别图片、样式文件
declare module "*.css" {
const classes: { [key: string]: string };
export default classes;
}
declare module "*.png" {
const src: string;
export default src;
}
declare module "*.svg" {
import { FC, SVGProps } from "react";
const SVGComponent: FC<SVGProps<SVGSVGElement>>;
export default SVGComponent;
}
# 4.4 发布声明文件
{
"compilerOptions": {
"declaration": true, // 生成 .d.ts
"declarationDir": "./dist/types", // 声明文件输出目录
"declarationMap": true // 声明 sourcemap(IDE 跳转到源码)
}
}
// package.json
{
"main": "./dist/index.js",
"types": "./dist/types/index.d.ts" // 指定入口声明文件
}
# 5. Webpack 集成方案
# 5.1 ts-loader 方案
// webpack.config.js
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
}
]
},
resolve: {
extensions: [".ts", ".tsx", ".js"]
}
};
ts-loader 特点:直接使用 tsc 编译,忠实于 tsconfig,慢但类型检查完整。
# 5.2 babel-loader + @babel/preset-typescript
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
"@babel/preset-typescript"
]
}
}
}
]
}
};
Babel 特点:快但不做类型检查,适合开发阶段(用 tsc --noEmit 单独做类型检查)。
# 5.3 选型建议
你的场景?
├── 类型检查严格,团队不要求极速构建 → ts-loader
├── 开发体验优先(HMR 快)→ babel-loader + tsc --noEmit(CI 中检查)
└── 第三方库开发 → ts-loader(保证 .d.ts 输出正确)
# 6. Vite / ESBuild 集成方案
# 6.1 Vite 开箱即用
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
// Vite 默认支持 TS——无需额外配置
plugins: [],
resolve: {
alias: {
"@": "/src"
}
}
});
Vite 的 TS 原理:
- 用 ESBuild 剥离类型(仅擦除,不做类型检查)→ 快
- 类型检查交给 IDE 或
tsc --noEmit --watch
# 6.2 类型检查插件
npm install -D vite-plugin-checker
import checker from "vite-plugin-checker";
export default defineConfig({
plugins: [
checker({
typescript: true // 浏览器中显示 TS 错误
})
]
});
# 7. 构建产物与发布
# 7.1 多目标构建
// tsconfig.cjs.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"outDir": "./dist/cjs"
}
}
// tsconfig.esm.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"outDir": "./dist/esm"
}
}
# 7.2 package.json 导出映射
{
"name": "my-lib",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
}
}
}
# 8. 速查表
| 选项 | 推荐值 | 作用 |
|---|---|---|
target | "ES2020" | 编译目标 JS 版本 |
module | "ESNext" | 模块系统(配合构建工具) |
strict | true | 全套严格检查 |
esModuleInterop | true | 兼容 CommonJS 默认导出 |
skipLibCheck | true | 跳过 .d.ts 检查(加速编译) |
forceConsistentCasingInFileNames | true | 文件名大小写一致 |
declaration | true | 生成 .d.ts |
sourceMap | true | 生成 sourcemap |
outDir | "./dist" | 输出目录 |
rootDir | "./src" | 源码目录 |
noEmit | true(纯检查) | 不生成产物 |
jsx | "react-jsx" | React 17+ JSX 模式 |
moduleResolution | "bundler"(TS5+) | 模块解析策略 |
项目初始化速查:
npx tsc --init # 生成 tsconfig.json 并预设常用选项
一句话总结:日常开发用 strict: true(全开),构建用 Vite/Babel(剥离类型+查错分离),发布库时带 .d.ts 文件。
专栏完结。回顾 8 篇:01.TS类型系统基础 → 02.TS接口与对象类型 → 03.TS函数与类实战 → 04.TS泛型编程实战 → 05.TS高级类型编程 → 06.TS类型守卫机制 → 07.TS模块系统详解 → 08.TS工程配置实践
上次更新: 2026/06/24, 12:59:24