主题
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.htmlsh
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。然后打开对应文档,按项目需要查找组件和用法,再在页面中组合使用。使用前要确认版本与项目依赖一致,使用中保持同一项目内组件风格尽量统一。笔记给出的入口是:
- 国内:
Ant Designhttps://ant-design.gitee.io/index-cn - 国外:
Material UIhttps://www.mdui.org/design
易错点
第一,把组件库当成业务逻辑的替代品,导致业务代码和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. 用 Webpack 打包 TS 文件
目录结构:
- src
- index.ts
- public
- index.html
- webpack.config.js
- tsconfig.jsonjs
// 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,或换网络环境重装)。
常见错误
- 现象:
.ts文件未被编译,报语法错误或 bundle 中无 TS 代码。原因:ts-loader未安装、module.rules.test写错或resolve.extensions缺少.ts。修法:确认安装ts-loader/typescript,检查test: /\.tsx?$/、use: 'ts-loader'与extensions。 - 现象:执行
npx webpack-dev-server提示命令不存在或服务起不来。原因:未安装webpack-cli/webpack-dev-server,或脚本/端口配置冲突。修法:补齐依赖,检查devServer.port,必要时改用npx webpack serve。 - 现象:新增同名
interface后,原对象突然报缺少属性。原因:同名接口发生声明合并,不是后定义覆盖前定义。修法:合并为一个接口,或避免无意重名;实现对象需满足全部声明。 - 现象:同一响应接口限定不同
data时被迫写成any/unknown。原因:未把变化的数据类型抽成泛型参数。修法:改为IResponse<T>,使用处传IBook、ITodo等具体类型。 - 现象:函数内访问
x.length编译报错,调用处传数字也未被拦截。原因:泛型T未约束具备length能力。修法:使用T extends { length: number }或约束接口。 - 现象:安装第三方库后仍报“找不到名称/是否需要安装类型定义”。原因:声明文件未随包安装、镜像源缺失或
node_modules不完整。修法:手动安装@types/xxx、切换源、删除node_modules重装,或自定义.d.ts声明。
练习
- 检查并修正一份
webpack.config.js:要求能处理.ts/.tsx,且import './utils'可解析到utils.ts。 - 设计
IResponse<T>,分别约束“书籍响应”和“待办响应”,写出两个变量的类型声明。 - 写一个函数
fn<T extends { length: number }>(x: T),并说明传字符串、数组、数字时分别会发生什么。 - 某第三方库导入后 TS 报缺少类型声明,列出你的排查与修复顺序。
- 给一个包含同名接口、泛型接口、泛型约束错误的 TS 文件做错误清单,并标注优先修复项。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | ts-接口重复定义 | 02:22 |
| 2 | 泛型参数可以不止一个 | 04:38 |
| 3 | 泛型接口 | 06:33 |
| 4 | 泛型类 | 01:57 |
| 5 | 泛型约束 | 04:29 |
| 6 | ts声明文件说明 | 13:28 |
小结
- Webpack 接 TS 的最小闭环是:入口、
ts-loader、tsconfig.json、resolve.extensions同时正确。 - 开发服务与生产打包要分别验证,避免只验证
devServer而漏掉build。 - 同名
interface是声明合并,不是覆盖;公共类型命名要控制冲突。 - 泛型用于表达“结构固定、局部类型变化”,变化点应抽成类型参数,返回结构要精确。
- 泛型接口、泛型类、泛型约束分别解决响应包装、容器类型、能力限定问题。
- 第三方库类型优先随包安装或
@types;缺失时再自定义.d.ts。 - 排错顺序建议:依赖与配置 → 声明文件 → 业务类型 → 构建产物。