01 · NOTES / WebGIS 开发
第 22 节 · 函数与泛型入门
2026 年 9 月 1 日
第 22 节 · 函数与泛型入门
📌 版本信息:基于 TypeScript 5.x(2026-08-29 核对) 📚 来源:TS 手册 · 函数 | TS 手册 · 泛型 | 阮一峰 TS 教程 · 泛型
一、这一节的目标
- 掌握函数类型的两种标注方式(含回调签名)
- 掌握可选参数、默认参数、参数解构的标注
- 理解泛型要解决的问题:一个函数适配多种类型还不出错
- 会写
<T>、会加泛型约束extends - 能读懂第三方库里的泛型(
Map<string, City>这类)
二、函数的类型标注
// ── 写法 ①:声明处逐个标注 ──
function haversine(a: Coord, b: Coord): number {
return 6371 * c; // Coord 是第 21 节的元组类型
}
// ── 写法 ②:函数类型别名(把"长这样的函数"起名)──
type DistanceFn = (a: Coord, b: Coord) => number;
const haversine2: DistanceFn = (a, b) => {
// 参数类型自动获得(上下文推断),不用再标
return 6371;
};
// ── 回调签名:接收函数当参数 ──
function processFeatures(features: QuakeFeature[], transform: (f: QuakeFeature) => string): string[] {
return features.map(transform); // 第 13 节的 map,现在你能写它了!
}
// ── 可选参数(?)与默认参数 ──
function flyTo(lat: number, lng: number, zoom?: number): void { /* … */ }
function formatCoord(lat: number, lng: number, digits = 4): string { /* … */ }
// 调用:flyTo(30.59, 114.3) ✅ flyTo(30.59, 114.3, 12) ✅
⚠️ TS 会检查实参个数:
flyTo(30.59)直接报错(JS 里默默 undefined,TS 当场抓)。
三、泛型:类型的"参数"
痛点:写一个"取数组第一项"的函数,类型怎么标?
function firstWrong(items: any[]): any { // ❌ 用 any:调用方拿到的也是 any
return items[0]; // 类型信息全丢,后面全靠猜
}
泛型 = 给类型留一个"占位符" T,调用时才确定:
function first<T>(items: T[]): T | undefined { // T:调用时填充的类型变量
return items[0];
}
const c = first(['武汉', '西安']); // T 被推断为 string → c: string | undefined
const n = first([30.59, 31.23]); // T 被推断为 number
first<string>(); // 也可以手动指定
c 的类型是 string | undefined——它提醒你数组可能为空(TS 标准库就是这么干的)。这是 any 永远给不了的安全性。
泛型约束 extends:给 T 立规矩
// 痛点:想取 .name 属性,但 T 可以是任何东西,TS 不敢保证有 name
function getNames<T extends { name: string }>(items: T[]): string[] {
// ↑ 约束:T 必须至少有 name: string 字段
return items.map((it) => it.name); // ✅ 有了约束,访问 .name 才合法
}
getNames(cities); // ✅ cities 的元素都有 name
getNames([1, 2, 3]); // ❌ number 没有 name——当场拦截
常见形态认脸(读第三方库必备)
Map<string, City> // 键值对:字符串键 → City 值(JS 的 Map 集合)
Promise<QuakeCollection> // "未来会给出"的 GeoJSON(fetch 返回的样子)
Partial<MapConfig> // MapConfig 所有字段变可选(第 38 节的工具类型)
Record<LayerType, string> // 以 LayerType 联合的每个值为键的对象
看到尖括号不再绕道——里面只是"类型的参数"。
四、动手跟练:22 · 泛型小工具
配套文件:02-web-basics/examples/22-泛型小工具.html
步骤:
- 打开文件读 6 道题:从"补标注"到"写泛型"渐进
- 核心三题:泛型化
first(对比 any 版的差别)、带约束的pluck(从对象数组里抽字段)、groupBy(按字段分组,GIS 统计的常见操作) - 全部在 Playground 过编译 + 故意传错类型验证拦截
通关标准:
- 能不看资料写出
<T>(items: T[]): T | undefined - 能说出
T extends { name: string }里 extends 的含义 - 认脸测试全对:给 5 个带尖括号的声明说出含义
五、自测题
- 函数返回 void 和返回 undefined 的类型标注有什么实际差别?
- 不用泛型、用 any 写 first(),调用方会失去什么?
<T extends { id: number }>(xs: T[])的含义?Promise<QuakeCollection>怎么读?- 泛型的类型参数名必须叫 T 吗?
参考答案
: void表示"调用方不该使用返回值"(返回什么都被禁止使用);返回undefined的函数调用结果只能赋给 undefined 类型——两者都约束调用方,语义略不同。- 调用方拿到的值类型是 any:失去补全、失去检查,undefined 的情况也不再被提醒。
- T 必须满足"至少有 id: number 字段"的约束,函数内可安全访问 x.id。
- 一个"最终会兑现为 QuakeCollection 的异步凭证"——await 它得到 QuakeCollection。
- 不必须,T 只是惯例(Type 的首字母);多个参数时常叫 U、V、K、V 等,语义化命名如
<TKey, TValue>更可读。
六、下一步
类型系统会用了 → 第 23 节:TS 在 Vite 项目中,真实项目里 TS 怎么配置、报错怎么读。
TAGSweb