Skip to content

第一章:快速入门 ​

1. TypeScript 简介 ​

1.1 TypeScript 是什么? ​

TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的一个超集,主要特点包括:

  1. 类型系统:为 JavaScript 添加了静态类型定义
  2. 完全兼容:任何有效的 JavaScript 代码都是有效的 TypeScript 代码
  3. 编译执行:TS 代码需要通过编译器转换为 JS 才能执行
  4. 增强功能:引入了类、接口、泛型等面向对象特性

1.2 为什么选择 TypeScript? ​

特性优点
静态类型检查在编译阶段发现错误,减少运行时异常
代码可维护性类型注解使代码结构更清晰,便于团队协作
更好的 IDE 支持智能提示、代码补全、重构工具更强大
渐进式采用可以逐步将 JS 项目迁移到 TS
现代 JavaScript 特性支持 ES6+ 特性并编译到旧版本浏览器

2. 开发环境搭建 ​

2.1 安装步骤 ​

  1. 安装 Node.js

  2. 安装 TypeScript 编译器

    bash
    npm install -g typescript
  3. 验证安装

    bash
    tsc --version

2.2 第一个 TypeScript 程序 ​

  1. 创建 hello.ts 文件:

    typescript
    function greet(name: string): string {
      return `Hello, ${name}!`;
    }
    
    console.log(greet("TypeScript"));
  2. 编译 TypeScript:

    bash
    tsc hello.ts
  3. 运行 JavaScript:

    bash
    node hello.js

3. 基础类型系统 ​

3.1 类型声明语法 ​

typescript
// 变量类型声明
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 可以自动推断类型:

typescript
let message = "Hello World"; // 推断为 string
let count = 42; // 推断为 number
let items = [1, 2, 3]; // 推断为 number[]

3.3 完整类型表 ​

类型示例描述使用场景
number1, -5.3, 0xFF所有数字类型数学运算、计数
string"text", 'text', \text``字符串类型文本处理、显示
booleantrue, false布尔值条件判断、开关
anylet x: any = 4;任意类型迁移旧代码、第三方库
unknownlet x: unknown = 4;类型安全的 any替代 any 类型
voidfunction(): void {}无返回值函数没有返回值
neverfunction(): never {}永不返回抛出异常、无限循环
object{key: value}非原始类型通用对象类型
arraynumber[], Array<number>数组类型列表数据存储
tuple[string, number]固定长度数组键值对、特定结构
enumenum Color {Red, Green}枚举类型命名常量集合
字面量其本身限制变量的值就是该字面量的值使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围,字面量类似于常量,一旦被定义后,就不能修改

3.4 类型详解 ​

3.4.1 number 类型 ​

typescript
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;

3.4.2 boolean 类型 ​

typescript
let isDone: boolean = false;

3.4.3 string 类型 ​

typescript
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 字面量类型 ​

typescript
// 精确值类型
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 联合类型与交叉类型 ​

使用 | 来连接多个类型 (联合类型),使用 & 表示同时满足(交叉类型)

typescript
// 联合类型:可以是多种类型之一
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,它不能直接赋值给其他变量,必须进行类型判断或者类型断言后才能赋值

typescript
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 类型 ​

用来表示空,以函数为例,就表示函数没有返回值

typescript
let unusable: void = undefined;
typescript
// 没有返回值
function fun(): void {}
typescript
// 返回值是 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 类型

typescript
let d: any = 4;
d = "hello";
d = true;
typescript
// 明变量如果不指定类型,则ts解析器会自动判断变量的类型为any(隐式any)
let d;
d = 10;
d = "test";
d = true;

3.4.9 never 类型的应用 ​

never 表示永远不会返回结果,一般用于函数抛出异常

typescript
// 场景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 类型详解 ​

typescript
// 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;
}; // 不允许
typescript
// 基础对象类型
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 数组与元组 ​

元祖就是固定长度的数组

typescript
// 数组的多种声明方式
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 枚举类型进阶 ​

typescript
// 数字枚举(默认从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编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:

第一种

typescript
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;

第二种

typescript
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;

3.4.14 类型别名 ​

typescript
// 类型的别名
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; // 不允许
typescript
// 基础类型别名
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>; // false

4. 编译配置详解 ​

4.1 基本编译命令 ​

bash
# 编译单个文件
tsc app.ts

# 监视模式(自动重新编译)
tsc app.ts --watch
tsc app.ts -w

# 编译整个项目
tsc

# 指定配置文件
tsc --project tsconfig.prod.json

4.2 tsconfig.json 完整配置示例 ​

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 不同环境的配置 ​

json
// 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。

  1. 初始化项目

    • 主要作用:创建package.json文件
    • 进入项目根目录,执行命令 npm init -y
  2. 下载构建工具

    shell
    npm 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中的清除插件,每次构建都会先清除目录
  3. 根目录下创建webpack的配置文件webpack.config.js

javascript
// 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",
    },
  };
};
  1. 根目录下创建tsconfig.json,配置可以根据自己需要修改

    json
    {
      "compilerOptions": {
        "target": "ES2015",
        "module": "ES2015",
        "strict": true
      }
    }
  2. 修改 package.json 脚本配置

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:

javascript
module.exports = {
  plugins: [
    require("autoprefixer"),
    require("cssnano")({
      preset: "default",
    }),
  ],
};
  1. 在src下创建ts文件,并在并命令行执行npm run build对代码进行编译,或者执行npm start来启动开发服务器

5.2 Babel 基础使用 ​

经过一系列的配置,使得 TS 和webpack已经结合到了一起,除了webpack,开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中。

  1. 安装依赖包:

    shell
    npm 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语法
  2. 修改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中指定要兼容的浏览器版本。

js
// 引入一个包
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 高级配置 ​

javascript
// 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,
  };
};

Released under the MIT License.