第一章:快速入门
1. TypeScript 简介
1.1 TypeScript 是什么?
TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的一个超集,主要特点包括:
- 类型系统:为 JavaScript 添加了静态类型定义
- 完全兼容:任何有效的 JavaScript 代码都是有效的 TypeScript 代码
- 编译执行:TS 代码需要通过编译器转换为 JS 才能执行
- 增强功能:引入了类、接口、泛型等面向对象特性
1.2 为什么选择 TypeScript?
| 特性 | 优点 |
|---|---|
| 静态类型检查 | 在编译阶段发现错误,减少运行时异常 |
| 代码可维护性 | 类型注解使代码结构更清晰,便于团队协作 |
| 更好的 IDE 支持 | 智能提示、代码补全、重构工具更强大 |
| 渐进式采用 | 可以逐步将 JS 项目迁移到 TS |
| 现代 JavaScript 特性 | 支持 ES6+ 特性并编译到旧版本浏览器 |
2. 开发环境搭建
2.1 安装步骤
安装 Node.js
- 官网下载
- 建议使用 LTS 版本
安装 TypeScript 编译器
bashnpm install -g typescript验证安装
bashtsc --version
2.2 第一个 TypeScript 程序
创建
hello.ts文件:typescriptfunction greet(name: string): string { return `Hello, ${name}!`; } console.log(greet("TypeScript"));编译 TypeScript:
bashtsc hello.ts运行 JavaScript:
bashnode hello.js
3. 基础类型系统
3.1 类型声明语法
// 变量类型声明
let username: string = "Alice";
let age: number = 25;
let isActive: boolean = true;
// 函数类型声明
function add(x: number, y: number): number {
return x + y;
}
// 箭头函数类型
const multiply = (a: number, b: number): number => a * b;3.2 类型推断
TypeScript 可以自动推断类型:
let message = "Hello World"; // 推断为 string
let count = 42; // 推断为 number
let items = [1, 2, 3]; // 推断为 number[]3.3 完整类型表
| 类型 | 示例 | 描述 | 使用场景 |
|---|---|---|---|
number | 1, -5.3, 0xFF | 所有数字类型 | 数学运算、计数 |
string | "text", 'text', \text`` | 字符串类型 | 文本处理、显示 |
boolean | true, false | 布尔值 | 条件判断、开关 |
any | let x: any = 4; | 任意类型 | 迁移旧代码、第三方库 |
unknown | let x: unknown = 4; | 类型安全的 any | 替代 any 类型 |
void | function(): void {} | 无返回值 | 函数没有返回值 |
never | function(): never {} | 永不返回 | 抛出异常、无限循环 |
object | {key: value} | 非原始类型 | 通用对象类型 |
array | number[], Array<number> | 数组类型 | 列表数据存储 |
tuple | [string, number] | 固定长度数组 | 键值对、特定结构 |
enum | enum Color {Red, Green} | 枚举类型 | 命名常量集合 |
字面量 | 其本身 | 限制变量的值就是该字面量的值 | 使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围,字面量类似于常量,一旦被定义后,就不能修改 |
3.4 类型详解
3.4.1 number 类型
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;3.4.2 boolean 类型
let isDone: boolean = false;3.4.3 string 类型
let color: string = "blue";
color = "red";
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.
I'll be ${age + 1} years old next month.`;3.4.4 字面量类型
// 精确值类型
let direction: "north" | "south" | "east" | "west";
direction = "north"; // ✅ 正确
direction = "up"; // ❌ 错误
// 数字字面量
let diceRoll: 1 | 2 | 3 | 4 | 5 | 6;
diceRoll = 3; // ✅ 正确
diceRoll = 7; // ❌ 错误3.4.5 联合类型与交叉类型
使用 | 来连接多个类型 (联合类型),使用 & 表示同时满足(交叉类型)
// 联合类型:可以是多种类型之一
type ID = string | number;
let userId: ID = 123; // ✅
userId = "abc-123"; // ✅
// 交叉类型:同时满足多种类型
interface Person {
name: string;
age: number;
}
interface Employee {
company: string;
position: string;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Alice",
age: 30,
company: "Tech Corp",
position: "Developer",
};3.4.6 unknown 类型与类型守卫
unknown表示未知类型的值,unknown 实际上就是一个 类型安全的any,它不能直接赋值给其他变量,必须进行类型判断或者类型断言后才能赋值
let userInput: unknown;
let userName: string;
// ❌ 错误:不能直接赋值
// userName = userInput;
// ✅ 正确:使用类型守卫
if (typeof userInput === "string") {
userName = userInput;
}
// ✅ 正确:使用类型断言
userName = userInput as string;
userName = <string>userInput; // 另一种语法3.4.7 void 类型
用来表示空,以函数为例,就表示函数没有返回值
let unusable: void = undefined;// 没有返回值
function fun(): void {}// 返回值是 boolean 类型
function fun1(): boolean {
return true;
}
// 返回值是 true | 1
function fun2(a: number) {
if (a === 1) {
return true;
} else {
return 1;
}
}3.4.8 any 类型
any 表示的是任意类型,一个变量设置类型为 any 后,相当于对该变量关闭了ts的类型检测。
使用 ts时,不建议使用 any 类型
let d: any = 4;
d = "hello";
d = true;// 明变量如果不指定类型,则ts解析器会自动判断变量的类型为any(隐式any)
let d;
d = 10;
d = "test";
d = true;3.4.9 never 类型的应用
never 表示永远不会返回结果,一般用于函数抛出异常
// 场景1:总是抛出异常
function throwError(message: string): never {
throw new Error(message);
}
// 场景2:无限循环
function infiniteLoop(): never {
while (true) {
// 执行某些操作
}
}
// 场景3:类型检查的穷尽性检查
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * 10 * 10;
case "square":
return 10 * 10;
case "triangle":
return 0.5 * 10 * 10;
default:
// 如果 Shape 类型扩展了但这里没处理,TypeScript 会报错
const exhaustiveCheck: never = shape;
return exhaustiveCheck;
}
}3.4.10 object 类型详解
// object 表示一个js对象(不常用)
let obj: object;
obj = {}; // 允许
obj = function () {}; // 允许
// {} 用来指定对象中可以包含哪些属性
// 语法:{属性名 :属性值,属性名 : 属性值}
let obj1: { name: string };
obj1 = {}; // 不允许
obj1 = { name: "孙悟空" }; // 允许
obj1 = { name: "孙悟空", age: 18 }; // 不允许
// 在属性名后边加上?,表示属性是可选的
let obj2: { name: string; age?: number };
obj2 = { name: "孙悟空" }; // 允许
obj2 = { name: "孙悟空", age: 18 }; // 允许
// [propName:string]:any 表示任意类型的属性
let obj3: { name: string; [propName: string]: any };
obj3 = { name: "猪八戒", age: 18, gender: "男" };
// 设置函数的结构的类型声明
/**
* 语法:(形参:类型, 形参:类型,...) => 返回值
*/
let fun3: (a: number, b: number) => number;
fun3 = function () {}; // 不允许
fun3 = function (a: number, b: number): number {
return a + b;
}; // 允许
fun3 = function (a: number, b: number, c: number): number {
return a + b + c;
}; // 不允许// 基础对象类型
let person: {
name: string;
age: number;
isStudent?: boolean; // 可选属性
readonly id: number; // 只读属性
[key: string]: any; // 索引签名
} = {
name: "Bob",
age: 25,
id: 12345,
};
// 函数类型定义
type MathFunction = (x: number, y: number) => number;
const add: MathFunction = (a, b) => a + b;
const multiply: MathFunction = (a, b) => a * b;
// 构造签名
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number,
): ClockInterface {
return new ctor(hour, minute);
}3.4.11 数组与元组
元祖就是固定长度的数组
// 数组的多种声明方式
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
let list3: (number | string)[] = [1, "two", 3];
// 只读数组
let readonlyArray: ReadonlyArray<number> = [1, 2, 3];
// readonlyArray.push(4); // ❌ 错误:只读数组不能修改
// 元组:固定长度和类型的数组
let coordinates: [number, number] = [40.7128, -74.006];
let personInfo: [string, number, boolean] = ["Alice", 30, true];
// 带可选元素的元组
let optionalTuple: [string, number?][] = [
["Alice", 30],
["Bob"], // 第二个元素可选
];
// 元组的剩余元素
type StringNumberBooleans = [string, number, ...boolean[]];
const snb: StringNumberBooleans = ["hello", 1, true, false, true];3.4.12 枚举类型进阶
// 数字枚举(默认从0开始)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
// 字符串枚举
enum MediaType {
JSON = "application/json",
XML = "application/xml",
PDF = "application/pdf",
}
// 常量枚举(编译时会被完全移除)
const enum Size {
Small = "S",
Medium = "M",
Large = "L",
}
// 计算的和常量成员
enum FileAccess {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
}
// 反向映射
enum Enum {
A,
}
let a = Enum.A; // 0
let nameOfA = Enum[a]; // "A"3.4.13 类型断言
有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
第一种
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;第二种
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;3.4.14 类型别名
// 类型的别名
type myType = string;
let m: myType;
m = "hello"; // 允许
m = 123; // 不允许
type myType1 = 1 | 2 | 3 | 4 | 5;
let l: myType1;
l = 1; // 允许
l = 2; // 允许
l = 3; // 允许
l = 4; // 允许
l = 5; // 允许
l = 6; // 不允许// 基础类型别名
type StringOrNumber = string | number;
type Callback<T> = (data: T) => void;
type Nullable<T> = T | null;
// 复杂类型组合
type Vehicle = {
brand: string;
year: number;
};
type Car = Vehicle & {
doors: number;
type: "sedan" | "suv" | "truck";
};
// 条件类型
type IsString<T> = T extends string ? true : false;
type Result1 = IsString<string>; // true
type Result2 = IsString<number>; // false4. 编译配置详解
4.1 基本编译命令
# 编译单个文件
tsc app.ts
# 监视模式(自动重新编译)
tsc app.ts --watch
tsc app.ts -w
# 编译整个项目
tsc
# 指定配置文件
tsc --project tsconfig.prod.json4.2 tsconfig.json 完整配置示例
{
"compilerOptions": {
/* 基本选项 */
"target": "ES2020", // 编译目标版本
"module": "ESNext", // 模块系统
"lib": ["ES2020", "DOM", "DOM.Iterable"], // 包含的库文件
"allowJs": true, // 允许编译 JS 文件
"checkJs": true, // 检查 JS 文件类型
"jsx": "react", // JSX 支持
"declaration": true, // 生成声明文件
"declarationMap": true, // 声明文件 sourcemap
"sourceMap": true, // 生成 sourcemap
"outFile": "./dist/bundle.js", // 合并输出文件
"outDir": "./dist", // 输出目录
"rootDir": "./src", // 根目录
"removeComments": true, // 移除注释
/* 严格类型检查选项 */
"strict": true, // 启用所有严格检查
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true, // 严格空值检查
"strictFunctionTypes": true, // 严格函数类型检查
"strictBindCallApply": true, // 严格 bind/call/apply 检查
"strictPropertyInitialization": true, // 严格属性初始化
"noImplicitThis": true, // 禁止隐式 this
"alwaysStrict": true, // 总是使用严格模式
/* 额外检查 */
"noUnusedLocals": true, // 检查未使用的局部变量
"noUnusedParameters": true, // 检查未使用的参数
"noImplicitReturns": true, // 检查函数是否隐式返回
"noFallthroughCasesInSwitch": true, // 检查 switch 语句的 fallthrough
/* 模块解析选项 */
"moduleResolution": "node", // 模块解析策略
"baseUrl": "./", // 基础路径
"paths": {
// 路径映射
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
},
"rootDirs": ["src", "generated"], // 多根目录
"typeRoots": ["./typings", "./node_modules/@types"], // 类型声明根目录
"types": ["node", "jest"], // 包含的类型声明包
"allowSyntheticDefaultImports": true, // 允许默认导入
"esModuleInterop": true, // 启用 ES 模块互操作
"preserveSymlinks": true, // 保留符号链接
/* 实验性选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true, // 生成装饰器元数据
/* 高级选项 */
"skipLibCheck": true, // 跳过库文件检查
"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
"resolveJsonModule": true, // 解析 JSON 模块
"isolatedModules": true, // 隔离模块
"allowUmdGlobalAccess": true // 允许访问 UMD 全局变量
},
/* 文件包含/排除 */
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"],
/* 需要排除在外的目录 */
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"],
/* 扩展配置 */
"extends": "./tsconfig.base.json",
/* 文件引用: 指定被编译文件的列表,只有需要编译的文件少时才会用到 */
"files": ["src/core.ts", "src/main.ts"],
/* 编译特定选项 */
"references": [{ "path": "./common" }, { "path": "./services" }],
/* 构建选项 */
"buildOptions": {
"incremental": true, // 增量编译
"tsBuildInfoFile": "./dist/.tsbuildinfo", // 构建信息文件
"disableSolutionSearching": true, // 禁用解决方案搜索
"disableReferencedProjectLoad": true // 禁用引用项目加载
}
}4.3 不同环境的配置
// tsconfig.prod.json - 生产环境配置
{
"extends": "./tsconfig.json",
"compilerOptions": {
"removeComments": true,
"noEmitOnError": true,
"sourceMap": false,
"declaration": false
}
}
// tsconfig.dev.json - 开发环境配置
{
"extends": "./tsconfig.json",
"compilerOptions": {
"removeComments": false,
"sourceMap": true,
"incremental": true
}
}5. 构建工具集成
5.1 Webpack 集成配置
通常情况下,实际开发中我们都需要使用构建工具对代码进行打包,TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS。
初始化项目
- 主要作用:创建package.json文件
- 进入项目根目录,执行命令
npm init -y
下载构建工具
shellnpm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin共安装了7个包:
- webpack:构建工具webpack
- webpack-cli:webpack的命令行工具
- webpack-dev-server:webpack的开发服务器
- typescript:ts编译器
- ts-loader:ts加载器,用于在webpack中编译ts文件
- html-webpack-plugin:webpack中html插件,用来自动创建html文件
- clean-webpack-plugin:webpack中的清除插件,每次构建都会先清除目录
根目录下创建webpack的配置文件webpack.config.js
// webpack.config.js - 完整配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
module.exports = (env, argv) => {
const isProduction = argv.mode === "production";
return {
// 入口配置
entry: {
main: "./src/index.ts",
vendor: ["react", "react-dom"],
},
// 输出配置
output: {
path: path.resolve(__dirname, "dist"),
filename: isProduction ? "[name].[contenthash].js" : "[name].js",
chunkFilename: isProduction
? "[name].[contenthash].chunk.js"
: "[name].chunk.js",
publicPath: "/",
globalObject: "this",
},
// 开发工具
devtool: isProduction ? "source-map" : "cheap-module-eval-source-map",
// 开发服务器
devServer: {
contentBase: "./dist",
hot: true,
open: true,
port: 3000,
historyApiFallback: true,
compress: true,
stats: "minimal",
},
// 解析配置
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
alias: {
"@": path.resolve(__dirname, "src"),
"@components": path.resolve(__dirname, "src/components"),
},
modules: [path.resolve(__dirname, "src"), "node_modules"],
},
// 模块规则
module: {
rules: [
// TypeScript 规则
{
test: /\.tsx?$/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
targets: {
browsers: ["last 2 versions", "> 1%", "not dead"],
},
useBuiltIns: "usage",
corejs: 3,
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-syntax-dynamic-import",
],
},
},
],
exclude: /node_modules/,
},
// CSS 规则
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
modules: {
auto: true,
localIdentName: isProduction
? "[hash:base64]"
: "[path][name]__[local]",
},
},
},
"postcss-loader",
],
},
// 图片资源
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
name: "assets/[name].[hash:8].[ext]",
},
},
],
},
// 字体文件
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ["file-loader"],
},
],
},
// 插件配置
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "./public/index.html",
favicon: "./public/favicon.ico",
minify: isProduction
? {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
}
: undefined,
}),
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: "./tsconfig.json",
diagnosticOptions: {
semantic: true,
syntactic: true,
},
},
}),
],
// 优化配置
optimization: {
minimize: isProduction,
splitChunks: {
chunks: "all",
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all",
},
commons: {
name: "commons",
minChunks: 2,
chunks: "async",
reuseExistingChunk: true,
},
},
},
runtimeChunk: {
name: "runtime",
},
},
// 性能提示
performance: {
hints: isProduction ? "warning" : false,
maxAssetSize: 244 * 1024,
maxEntrypointSize: 244 * 1024,
},
// 外部依赖
externals: {
jquery: "jQuery",
},
};
};根目录下创建tsconfig.json,配置可以根据自己需要修改
json{ "compilerOptions": { "target": "ES2015", "module": "ES2015", "strict": true } }修改 package.json 脚本配置
{
"scripts": {
"start": "cross-env NODE_ENV=development webpack serve --config webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"build:analyze": "cross-env NODE_ENV=production ANALYZE=true webpack --config webpack.config.js",
"test": "jest",
"test:coverage": "jest --coverage",
"lint": "eslint src/**/*.{ts,tsx}",
"lint:fix": "eslint src/**/*.{ts,tsx} --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
}
}postcss.config.js:
module.exports = {
plugins: [
require("autoprefixer"),
require("cssnano")({
preset: "default",
}),
],
};- 在src下创建ts文件,并在并命令行执行
npm run build对代码进行编译,或者执行npm start来启动开发服务器
5.2 Babel 基础使用
经过一系列的配置,使得 TS 和webpack已经结合到了一起,除了webpack,开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中。
安装依赖包:
shellnpm i -D @babel/core @babel/preset-env babel-loader core-js共安装了4个包,分别是:
- @babel/core:babel的核心工具
- @babel/preset-env:babel的预定义环境
- @babel-loader:babel在webpack中的加载器
- core-js:core-js用来使老版本的浏览器支持新版ES语法
修改webpack.config.js配置文件
json...略... module: { rules: [ { test: /\.ts$/, use: [ { loader: "babel-loader", options:{ presets: [ [ "@babel/preset-env", { "targets":{ "chrome": "58", "ie": "11" }, "corejs":"3", "useBuiltIns": "usage" } ] ] } }, { loader: "ts-loader", } ], exclude: /node_modules/ } ] } ...略...如此一来,使用ts编译后的文件将会再次被babel处理,使得代码可以在大部分浏览器中直接使用,可以在配置选项的targets中指定要兼容的浏览器版本。
// 引入一个包
const path = require("path");
// 引入 html-webpack-plugin 插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 引入 clean 插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
// 指定入口文件
entry: "./src/index.ts",
// 指定打包文件所在目录
output: {
// 指定打包后的目录
path: path.resolve(__dirname, "dist"),
// 打包后的文件名称
filename: "bundle.js",
// 告诉 webpack 不使用箭头函数
environment: {
arrowFunction: false,
},
},
// 指定 webpack 打包时要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 配置要使用的loader,按照数组从后往前执行
use: [
{
// 指定加载器
loader: "babel-loader",
// 设置babel
options: {
// 设置预定义的环境
presets: [
[
// 指定环境插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标浏览器
targets: {
chrome: "88",
ie: "11",
},
// 指定 corejs 的版本
corejs: "3",
// 使用corejs的方式,"usage"表示按需加载
useBuiltIns: "usage",
},
],
],
},
},
"ts-loader",
],
// 要排除的文件夹
exclude: /node_modules/,
},
],
},
// 配置 webpack插件
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
// title: '这是一个自定义的title',
template: "./src/index.html",
}),
],
// 用来设置引用模块
resolve: {
extensions: [".ts", ".js"],
},
};5.3 Babel 高级配置
// babel.config.js
module.exports = (api) => {
// 缓存配置
api.cache.using(() => process.env.NODE_ENV);
const presets = [
[
"@babel/preset-env",
{
targets: {
browsers: ["last 2 versions", "> 1%", "not dead", "not op_mini all"],
},
useBuiltIns: "usage",
corejs: { version: 3, proposals: true },
},
],
"@babel/preset-typescript",
"@babel/preset-react",
];
const plugins = [
// 提案特性
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
// 优化和转换
"@babel/plugin-transform-runtime",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-modules-commonjs",
// 开发工具
process.env.NODE_ENV === "development" && "react-refresh/babel",
].filter(Boolean);
return {
presets,
plugins,
// 排除第三方库的转换
exclude: /node_modules\/(?!(my-module|another-module)\/).*/,
// 源代码映射
sourceMaps: true,
retainLines: true,
};
};