Skip to content

Webpack 打包 TS 文件与创建typescript的(Day11 2/3) ​

本页对应 React + TypeScript + 医通项目,共 6 段视频,约 33 分钟。

学习目标 ​

  • 能说明 TypeScript 同名接口的合并规则,并编写同名 interface 验证实现对象必须包含合并后的全部属性。
  • 能定义并使用带多个泛型参数的函数,正确推导或显式传入多个类型参数,并说明返回值的元组类型。
  • 能针对响应结果中类型可变的 data 字段设计泛型接口,并用不同实体类型实例化以复用同一响应接口。
  • 能定义泛型类,在实例化时传入具体类型,并说明类型参数如何约束类成员或构造参数。
  • 能使用 extends 对泛型参数施加约束,使函数参数必须具备指定属性,并解释约束前后的编译报错差异。
  • 能说明 TypeScript 声明文件的作用,能为缺少类型声明的第三方库安装 @types 包或编写 .d.ts 自定义声明并通过编译检查。

知识点 ​

为什么用 Webpack 处理 TS 文件 ​

为什么需要:浏览器不能直接识别 .ts 文件,项目里写的 TS 必须先编译成 JS,再被打包进页面。Webpack 的作用是把入口、输出、开发服务、HTML 模板和 TS 编译串成一条构建链路。

怎么想:Webpack 本身不负责“看懂 TS”,它通过 ts-loader 调用 TypeScript 的编译能力。因此配置重点有两层:一层是 Webpack 的入口/输出/插件/开发服务;另一层是 TypeScript 的 tsconfig.json。转写稿里反复强调 TS 是在编译阶段做语法检查的,这一点放到 Webpack 里同样成立:ts-loader 接进来以后,类型检查仍按 TS 规则走,Webpack 不会替你绕过类型错误。

怎么做:先建目录,再安装 Webpack、ts-loader、typescript 等依赖。

text
- src
    - index.ts
- public
	- index.html
sh
npm init
npm i webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin typescript  -D

易错点:webpack-cli 是命令行工具,webpack-dev-server 是开发服务,typescript 是 ts-loader 的编译基础,不能漏装;这些通常放开发依赖,所以用 -D。另外,src/index.ts 要和后面 entry 的路径对应。

配置 webpack.config.js ​

为什么需要:只装依赖不会自动知道入口在哪里、产物放哪里、TS 用什么 loader、HTML 模板是谁。配置文件就是把这些约定写清楚。

怎么想:先让 TS 进入构建链路:entry 指向 ./src/index.ts,module.rules 用 ts-loader 处理 .ts/.tsx;再让产物有位置:output.path 指到 build,filename 指到 js/bundle.js;最后用 HtmlWebpackPlugin 以 public/index.html 为模板生成页面,并自动注入打包后的 JS。

怎么做:在 src 同级目录创建 webpack.config.js:

js
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/index.ts',
    output: {
        path: resolve(__dirname, 'build'),
        filename: 'js/bundle.js' 
    },
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader'
            }
        ]
    },
    resolve: {
        extensions: ['.ts','.tsx','.js']
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html'
        })
    ],
    devServer: {
        port: 3000,
        open: true
    }
}

易错点:test: /\.tsx?$/ 表示匹配 .ts 和 .tsx;use: 'ts-loader' 不要写成普通字符串数组以外的错误形式;output.path 要用绝对路径,所以这里用 resolve(__dirname, 'build');filename: 'js/bundle.js' 会在 build 下生成 js/bundle.js;resolve.extensions 让 import 时可以省略扩展名,顺序要包含 .ts、.tsx、.js;plugins 里必须 new HtmlWebpackPlugin(...)。

tsconfig.json 与第三方库声明文件 ​

为什么需要:ts-loader 需要 TypeScript 编译配置;项目根目录执行 tsc --init 会生成 tsconfig.json。更重要的是,TS 在编译阶段会检查类型,引入第三方库时,如果库没有类型声明,TS 就不认识它。

怎么想:转写稿里用 jQuery 说明过这个问题:直接写 $ 时,TS 可能提示找不到名称 $,并建议安装 @types/jquery。原因是 TS 不认识这个第三方库的函数或对象,需要“声明文件”告诉它这是什么。安装某些包时可能自动带声明文件;如果没有自动装上,就手动安装 @types/包名,或者自定义以 .d.ts 结尾的声明文件,用 declare 说明变量/函数类型。这个思路放到 Webpack 项目里一样成立:ts-loader 走的是 TS 编译流程,缺声明文件时不是 Webpack 配置错了,而是类型声明缺失。

怎么做:项目根目录创建 tsconfig.json:

text
tsc --init

需要时安装类型声明,例如转写稿中的 jQuery:

sh
npm i -D @types/jquery

也可以自定义声明文件,文件名以 .d.ts 结尾,例如声明 $ 是一个函数。转写稿强调:文件名不是关键,关键是 .d.ts 结尾;声明后 TS 才知道这个第三方变量是什么。

易错点:不要把“Webpack 能打包”误解成“TS 不检查类型”;@types 包没装、镜像源里没有、网络不好导致声明文件没下下来,都可能让项目报类型错误。转写稿给的处理思路是:先看是否提示安装 @types/xxx,能手动装就手动装;装不上就换源、换网络,或删除 node_modules 后重新 npm i。带斜杠的包名安装 @types 时要注意包名写法,按报错提示处理,不要凭感觉拼。

启动开发服务与打包 ​

为什么需要:开发时需要本地服务、自动打开页面;上线前需要生成静态产物。

怎么想:webpack-dev-server 用于开发调试,webpack 用于真正打包。两者都通过 npx 调用本地依赖。

怎么做:

sh
npx webpack-dev-server          # 启动开发服务
npx webpack                     # 打包代码

易错点:npx 会优先找本地 node_modules/.bin;端口 3000 被占用时要换端口;open: true 会自动打开浏览器;打包后检查 build 目录和 HTML 中注入的 js/bundle.js 是否正确。调试时先跑 npx webpack 看编译错误,再开 devServer,这样更容易区分是路径问题、loader 问题还是 TS 类型/声明文件问题。

本节目标与创建命令 ​

本节笔记只给出一个目标:创建一个 TypeScript 版本的 React 项目。为什么需要先做这一步?因为 React 项目默认可以是 JavaScript,若想让项目从创建时就具备 TS 的类型检查能力,最省事的方式是直接用官方脚手架的类型模板,而不是先建 JS 项目再手工改配置。怎么想?把“项目名”和“模板类型”作为两个关键信息:项目名决定目录名,--template typescript 决定使用 TypeScript 模板。怎么做?按笔记原样执行下面命令,npm 与 yarn 二选一即可:

shell

create-react-app 项目名 --template typescript

yarn create react-app 项目名 --template typescript

易错点:--template typescript 不要拼错;npm 和 yarn 不要在同一项目里混用;项目名不要与已有目录冲突。若本机没有全局 create-react-app 命令,应按 CRA 的常见用法补上 npx 或先确认脚手架是否可用,不要误以为模板参数写错。

为什么用 TypeScript 模板 ​

为什么需要模板?因为 TypeScript 项目需要 tsconfig、类型依赖和 .tsx 文件识别等配置,手工补容易漏。怎么想?创建阶段就把语言选对,后续写组件、接口、泛型时才有类型检查。怎么做?执行上面的命令后,进入项目目录并检查项目结构是否出现 TypeScript 相关文件。易错点:不要先创建 JavaScript 项目再“改名”成 TS;不要把 typescript 当成运行时库随意删掉;不要看到 .tsx 就以为所有第三方库都自动有类型。

创建后先验证什么 ​

为什么需要验证?因为命令执行成功不等于项目可运行。怎么想?先看依赖、配置、入口文件,再启动项目。怎么做?检查 package.json 中是否有 TypeScript 相关依赖,检查是否存在 tsconfig.json,检查 src 下是否有 .tsx 入口;然后按项目脚本启动。易错点:在错误目录执行启动命令;依赖未安装完整就启动;把 node_modules 的问题误判成代码问题。

从转写稿看:声明文件是常见调试点 ​

转写稿重点讲了“声明文件”,这正好是创建 TS React 项目后安装第三方库时最容易遇到的调试场景。为什么需要?TS 在编译阶段做语法检查,第三方库的函数或对象本身不是 TS 写的,TS 可能“不认识它们”。怎么想?要么让库自带类型声明,要么安装社区声明包,要么自己写 .d.ts 声明。怎么做?安装库时优先看它是否自动带类型;没有自动安装时,使用 npm i @types/包名 单独安装;仍不行时,在项目根目录新建以 .d.ts 结尾的文件,用 declare 说明导入对象的身份。转写稿中提到的 jQuery 就是典型例子:安装库后若类型声明也自动装上,$ 不再报“找不到名称”;若没有,就手动安装 @types/jquery,或自定义声明文件。易错点:把“声明文件”误写成“生命文件”;以为装了包就一定有类型;镜像源里没有声明文件、网速不好导致安装失败;带斜杠的包名安装 @types 时容易混淆。排查时先看报错是否提示安装 @types/...,再检查镜像源、网络和 node_modules,必要时删除 node_modules 后重装。

转写稿中的类型概念如何衔接 ​

转写稿还讨论了接口重复定义、泛型参数、泛型接口、泛型类和泛型约束。它们不是创建命令的一部分,而是项目创建后写 TS 代码会遇到的类型能力。为什么需要?因为 React 组件属性、接口响应数据、工具函数参数都可能类型不确定。怎么想?同名接口会合并而不是覆盖,所以实现时必须把同名接口的属性都写全;泛型参数本质是类型参数,可以不止一个;接口里某个 data 类型不确定时,可把接口做成泛型接口;类成员类型不确定时可用泛型类;需要限定“必须有某个属性”时用 T extends 接口 做泛型约束。怎么做?写类型时先用接口描述稳定字段,把变化字段抽成泛型参数;约束时让泛型继承一个含目标属性的接口。易错点:以为同名接口会覆盖;泛型参数随意写小写导致规范不统一;只写一个泛型却传了多个类型;泛型约束漏写导致函数内部访问 length 等属性时报错。把这些概念放在创建项目之后学习,能避免把“创建命令”和“类型系统”混为一谈。

理解UI组件库:从“重复写界面”到“复用通用能力” ​

本节笔记的核心只有两点:UI组件库封装了一些通用的界面功能效果,用来简化开发编码;在React生态中,笔记给出了两个代表库——国内的Ant Design和国外的Material UI。下面按“为什么需要 → 怎么想 → 怎么做 → 易错点”展开。需要说明的是:本节笔记未附图片和代码,因此正文不额外添加图片或代码;转写稿主要讲TypeScript的接口、泛型与声明文件,与“理解UI组件库”不是同一主题,故不把无关推导混入本节。

知识点一:UI组件库封装通用界面功能,简化开发编码 ​

为什么需要
做前端页面时,很多界面能力是重复出现的:按钮、表单、弹窗、表格、导航、提示等。如果每个项目都从零写一遍,不仅编码量大,还容易出现风格不统一、交互细节不一致、可访问性和边界状态处理不完整等问题。UI组件库的价值,就是把“通用的界面功能效果”提前封装好,让开发者把精力放在业务逻辑和页面组织上,而不是反复造轮子。笔记里“封装了一些通用的界面功能效果的组件,简化开发编码”说的就是这个意思。

怎么想
先判断哪些东西属于“通用界面功能”,哪些属于“业务专属逻辑”。通用界面功能适合交给组件库;业务数据、业务流程、权限规则等仍应由项目自己实现。理解组件库时,不要把它当成“万能业务框架”,而要把它看成一套可复用的界面能力集合。选择组件库时,优先看项目技术栈是否匹配、团队是否熟悉、文档是否易查、组件风格是否满足产品要求。

怎么做
在实际开发中,先根据项目使用的框架确定组件库范围。本节笔记明确限定在React生态:国内可关注Ant Design,国外可关注Material UI。然后打开对应文档,按项目需要查找组件和用法,再在页面中组合使用。使用前要确认版本与项目依赖一致,使用中保持同一项目内组件风格尽量统一。笔记给出的入口是:

易错点
第一,把组件库当成业务逻辑的替代品,导致业务代码和UI组件耦合过深。第二,同一项目里随意混用多个组件库,造成风格和交互不一致。第三,只看示例代码,不看当前版本文档,导致API对不上。第四,忽略项目技术栈,笔记说的是React UI组件库,不能脱离React语境随意套用。

知识点二:认识React生态中的两个代表库 ​

为什么需要
了解代表性组件库,有助于建立技术选型意识。笔记把Ant Design标为国内、把Material UI标为国外,并给出访问入口,目的不是让你死记名称,而是让你知道:React生态中已经有成熟的UI组件库可供选择,遇到通用界面需求时可以先查库,而不是从零实现。

怎么想
看到“国内/国外”的标注,应理解为笔记给出的代表库分类,而不是绝对的质量判断。选型时要结合团队习惯、产品风格、文档语言、维护成本和项目约束。对初学者来说,先跟着笔记给出的两个入口熟悉“组件库文档长什么样、组件如何组织、示例如何阅读”,比盲目比较参数更重要。

怎么做
按笔记提供的链接访问文档,先浏览组件库的整体结构,再带着具体界面需求去查对应组件。比如项目需要某种通用界面效果时,先判断它是否属于组件库已封装的通用能力;若是,就按文档示例接入;若不是,再考虑自定义实现。使用过程中记录项目采用的库和版本,避免团队成员各用各的。

易错点
不要把“国内/国外”当成唯一选型标准;不要看到链接就直接复制代码而不确认版本;不要因为某个库流行就在所有项目中强行使用;也不要把组件库文档中的示例当成业务代码成品,示例通常只演示通用能力,仍需要按业务需求做二次组织。

小结:本节应掌握什么 ​

本节要建立的是“组件库意识”:通用界面功能可以封装复用,React生态中有Ant Design和Material UI等代表库。学习时按“先判断通用需求 → 再查对应库文档 → 最后按项目版本接入”的顺序推进;同时警惕业务与UI过度耦合、混用组件库、忽略版本文档等易错点。

本节转写稿实际围绕 TypeScript 的接口合并、泛型与声明文件展开,并未涉及 Ant Design 组件用法;下面按转写稿内容整理。原始笔记未提供图片,故不插入图片,仅保留可还原的代码示意。

接口重复定义:同名 interface 会合并 ​

为什么需要:给同一个对象分多次补充类型时,希望后写的定义不要覆盖前面的定义。
怎么想:TypeScript 中同名 interface 不是覆盖,而是把属性合并成同一个接口的限定。
怎么做:

ts
interface ICar {
  name: string;
}
interface ICar {
  age: number;
}
const car: ICar = { name: "破二手车", age: 20 };

转写稿里的调试过程是:只写 name 会报错,因为 ICar 还要求 age;补上 age 后通过。
易错点:实现该接口时,同名接口里的属性都要写全;type 的同名别名不能像 interface 这样合并。

泛型参数可以不止一个 ​

为什么需要:一个函数里可能不止一个类型不确定,例如参数 A、B 的类型都要等调用时才知道。
怎么想:既然泛型是“类型的参数”,那就可以有多个类型参数;每个不确定类型对应一个泛型。
怎么做:

ts
function createTuple<T, D>(a: T, b: D): [T, D] {
  return [a, b];
}
const t = createTuple<number, string>(1, "abc");

返回类型 [T, D] 是元组:长度固定、每个元素类型固定。调用时传入 1 和 "abc",就能推断出 T 是 number、D 是 string。
易错点:泛型参数是类型参数,不是运行时值;命名通常用大写字母如 T、K、V、D,小写虽可能不报错,但不合规范。

泛型接口:把变化的 data 抽成类型参数 ​

为什么需要:服务端响应通常都有 code、msg、data,但不同接口的 data 可能是书,也可能是待办事项。若写死 data 类型,就不能复用同一个响应接口。
怎么想:code、msg 固定,只有 data 变化,就把 data 的类型抽成泛型 T。
怎么做:

ts
interface IResponse<T> {
  code: number;
  msg: string;
  data: T;
}
interface IBook { id: string; name: string; author: string; }
interface ITodo { id: string; title: string; isDone: boolean; }

const bookRes: IResponse<IBook> = {
  code: 200, msg: "成功",
  data: { id: "1", name: "葵花宝典", author: "东方不败" }
};
const todoRes: IResponse<ITodo> = {
  code: 200, msg: "获取数据成功",
  data: { id: "2", title: "吃饭", isDone: true }
};

易错点:使用时要传具体类型,如 IResponse<IBook>;不要把固定字段也泛型化,否则接口会失去约束力。

泛型类:类内部存储类型不确定时使用 ​

为什么需要:一个容器类可能装书,也可能装待办事项,存储元素类型在实例化时才确定。
怎么想:把类内部用到的类型抽成 T,在 new 时传入具体类型。
怎么做:

ts
class Store<T> {
  constructor(public store: T[]) {}
}
const bookStore = new Store<IBook>([
  { id: "1", name: "书", author: "作者" }
]);

易错点:泛型类约束的是类型,不是运行时值;实例化时要把具体类型传给 T,否则类型会退化为推断结果。

泛型约束:要求类型必须具备某种能力 ​

为什么需要:函数里访问 x.length,但任意类型不一定有 length,传数字就会报错。
怎么想:用 extends 约束 T 必须拥有 length 属性。
怎么做:

ts
interface Lengthwise { length: number; }
function fn2<T extends Lengthwise>(x: T) {
  return x.length;
}
fn2("abc"); // 可以
fn2([1, 2, 3]); // 可以
fn2(123); // 报错:number 不满足 Lengthwise

易错点:约束的是“类型必须具备的能力”,不是具体值;字符串和数组有 length,数字没有。

TS 声明文件:让 TS 认识第三方 JS 库 ​

为什么需要:TS 在编译阶段做语法检查,第三方 JS 库的函数或对象它可能不认识,例如 $。
怎么想:安装包时可能自带类型声明;若没有,就安装 @types/包名,或自定义 .d.ts。
怎么做:

ts
// 若报“找不到名称 $”
declare function $(selector: string): any;

也可安装类型包:

bash
npm i -D @types/jquery

转写稿中的调试过程是:未声明 $ 时报错;安装 @types/jquery 或写 declare 后报错消失;删掉声明又立刻报错。
易错点:自定义声明文件必须以 .d.ts 结尾,文件名不重要;类型包通常装成开发依赖;若镜像源没有类型包或网络失败,可换源、删 node_modules 后重装。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

1. 用 Webpack 打包 TS 文件 ​

目录结构:

- src
    - index.ts
- public
    - index.html
- webpack.config.js
- tsconfig.json
js
// webpack.config.js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.ts',
  output: {
    path: resolve(__dirname, 'build'),
    filename: 'js/bundle.js'
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader'
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
  ],
  devServer: {
    port: 3000,
    open: true
  }
};
ts
// src/index.ts
const msg: string = 'Hello TypeScript + Webpack';
document.body.innerHTML = `<h1>${msg}</h1>`;
html
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>webpack + ts</title>
  </head>
  <body></body>
</html>
sh
npm init
npm i webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin typescript -D
npx tsc --init

npx webpack-dev-server   # 启动开发服务
npx webpack              # 打包代码

要点:

  • entry 直接指向 .ts 文件,ts-loader 负责在打包时把 TS 编译成 JS。
  • resolve.extensions 必须带上 .ts、.tsx,否则 import './xxx' 找不到 TS 文件。
  • HtmlWebpackPlugin 用 public/index.html 做模板,打包后自动注入 js/bundle.js。
  • 易错点:忘记执行 npx tsc --init 生成 tsconfig.json,ts-loader 会报找不到配置;命令用 npx 调用本地安装的 webpack,不能直接写 webpack。

2. 同名接口、泛型参数、泛型接口、泛型类、泛型约束 ​

ts
// ===== 1. 同名接口会自动合并 =====
interface ICar {
  name: string;
}
interface ICar {
  age: number;
}
const car: ICar = { name: '破二手车', age: 20 }; // 两个属性都必须写

// ===== 2. 泛型参数可以不止一个 =====
function createArray<T, P>(a: T, b: P): [T, P] {
  return [a, b];
}
const tuple = createArray<number, string>(1, 'abc');

// ===== 3. 泛型接口 =====
interface IResponse<T> {
  code: number;
  msg: string;
  data: T;
}

interface IBook {
  id: string;
  name: string;
  author: string;
}

interface ITodo {
  id: string;
  title: string;
  isDone: boolean;
}

const data1: IResponse<IBook> = {
  code: 200,
  msg: '成功',
  data: { id: '1', name: '葵花宝典', author: '东方不败' }
};

const data2: IResponse<ITodo> = {
  code: 200,
  msg: '获取数据成功',
  data: { id: '2', title: '吃饭', isDone: true }
};

// ===== 4. 泛型类 =====
class Store<T> {
  store: T[] = [];
}

const s1 = new Store<IBook>();
s1.store.push({ id: '1', name: '葵花宝典', author: '东方不败' });

const s2 = new Store<ITodo>();
s2.store.push({ id: '2', title: '吃饭', isDone: true });

// ===== 5. 泛型约束 =====
interface LengthWise {
  length: number;
}

function fn2<T extends LengthWise>(x: T) {
  console.log(x.length);
}

fn2('abc');      // 可以,字符串有 length
fn2([1, 2, 3]);  // 可以,数组有 length
// fn2(123);     // 报错:number 没有 length 属性

要点:

  • 同名接口不会被覆盖,属性会合并成同一个类型,实现时所有同名接口的属性都得写全,否则报错。
  • 泛型就是“类型的形参”,既然参数可以多个,泛型也可以多个:<T, P>,调用 createArray<number, string> 时 T、P 才确定,返回值是元组 [T, P]。
  • 泛型接口解决的是“接口里只有某一项类型不确定”的场景:IResponse<T> 中 code、msg 固定,data 用 T,同一个接口就能限定 IBook 和 ITodo 两种响应体。
  • 泛型类:new Store<IBook>() 时把类型参数传给 T,store 数组里就只能装 IBook。
  • 泛型约束 T extends LengthWise:要求传入的类型必须具有 length 属性,传数字 123 会报错,传字符串、数组没问题。
  • 易错点:泛型参数规范上写大写字母(T、K、V、P 等),小写虽然不报错但不规范;extends 在这里是“约束”,不是类的继承。

3. 自定义声明文件(.d.ts) ​

ts
// 文件名随意,但必须以 .d.ts 结尾,放在项目根目录下
// 例如 jquery.d.ts

declare function $(selector: string): any;
ts
// src/index.ts
$('.box'); // 有了上面的声明,这里不再报错
sh
# 正常情况下安装第三方库时会自动带上类型声明文件;
# 如果没装上,需要手动安装 @types/包名
npm i @types/jquery -D

要点:

  • TS 在编译阶段做语法检查,第三方 JS 库的函数/对象它不认识,所以要用 declare 先“声明身份”,声明文件里只写类型不写实现。
  • 文件名不重要,关键是后缀必须是 .d.ts。
  • 安装 npm i jquery 时一般会自动把 @types/jquery 一起装上,所以 $ 不报错;如果只装了包没装类型,编辑器会提示 npm i --save-dev @types/jquery。
  • 易错点:包名本身带斜杠时,安装类型声明时斜杠要处理成双下划线,容易和 @types/ 后面的斜杠混淆。
  • 装不上类型声明文件的常见原因:当前 npm 镜像源里没有该声明文件(换源或换回官方源)、网速问题(删掉 node_modules 重新 npm i,或换网络环境重装)。

常见错误 ​

  1. 现象:.ts 文件未被编译,报语法错误或 bundle 中无 TS 代码。原因:ts-loader 未安装、module.rules.test 写错或 resolve.extensions 缺少 .ts。修法:确认安装 ts-loader/typescript,检查 test: /\.tsx?$/、use: 'ts-loader' 与 extensions。
  2. 现象:执行 npx webpack-dev-server 提示命令不存在或服务起不来。原因:未安装 webpack-cli/webpack-dev-server,或脚本/端口配置冲突。修法:补齐依赖,检查 devServer.port,必要时改用 npx webpack serve。
  3. 现象:新增同名 interface 后,原对象突然报缺少属性。原因:同名接口发生声明合并,不是后定义覆盖前定义。修法:合并为一个接口,或避免无意重名;实现对象需满足全部声明。
  4. 现象:同一响应接口限定不同 data 时被迫写成 any/unknown。原因:未把变化的数据类型抽成泛型参数。修法:改为 IResponse<T>,使用处传 IBook、ITodo 等具体类型。
  5. 现象:函数内访问 x.length 编译报错,调用处传数字也未被拦截。原因:泛型 T 未约束具备 length 能力。修法:使用 T extends { length: number } 或约束接口。
  6. 现象:安装第三方库后仍报“找不到名称/是否需要安装类型定义”。原因:声明文件未随包安装、镜像源缺失或 node_modules 不完整。修法:手动安装 @types/xxx、切换源、删除 node_modules 重装,或自定义 .d.ts 声明。

练习 ​

  1. 检查并修正一份 webpack.config.js:要求能处理 .ts/.tsx,且 import './utils' 可解析到 utils.ts。
  2. 设计 IResponse<T>,分别约束“书籍响应”和“待办响应”,写出两个变量的类型声明。
  3. 写一个函数 fn<T extends { length: number }>(x: T),并说明传字符串、数组、数字时分别会发生什么。
  4. 某第三方库导入后 TS 报缺少类型声明,列出你的排查与修复顺序。
  5. 给一个包含同名接口、泛型接口、泛型约束错误的 TS 文件做错误清单,并标注优先修复项。

原视频对照 ​

#视频时长
1ts-接口重复定义02:22
2泛型参数可以不止一个04:38
3泛型接口06:33
4泛型类01:57
5泛型约束04:29
6ts声明文件说明13:28

小结 ​

  1. Webpack 接 TS 的最小闭环是:入口、ts-loader、tsconfig.json、resolve.extensions 同时正确。
  2. 开发服务与生产打包要分别验证,避免只验证 devServer 而漏掉 build。
  3. 同名 interface 是声明合并,不是覆盖;公共类型命名要控制冲突。
  4. 泛型用于表达“结构固定、局部类型变化”,变化点应抽成类型参数,返回结构要精确。
  5. 泛型接口、泛型类、泛型约束分别解决响应包装、容器类型、能力限定问题。
  6. 第三方库类型优先随包安装或 @types;缺失时再自定义 .d.ts。
  7. 排错顺序建议:依赖与配置 → 声明文件 → 业务类型 → 构建产物。