返回笔记列表
01 · NOTES / WebGIS 开发

第 22 节 · 函数与泛型入门

2026 年 9 月 1 日

第 22 节 · 函数与泛型入门

📌 版本信息:基于 TypeScript 5.x(2026-08-29 核对) 📚 来源:TS 手册 · 函数TS 手册 · 泛型阮一峰 TS 教程 · 泛型

一、这一节的目标

  1. 掌握函数类型的两种标注方式(含回调签名)
  2. 掌握可选参数、默认参数、参数解构的标注
  3. 理解泛型要解决的问题:一个函数适配多种类型还不出错
  4. 会写 <T>、会加泛型约束 extends
  5. 能读懂第三方库里的泛型(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

步骤:

  1. 打开文件读 6 道题:从"补标注"到"写泛型"渐进
  2. 核心三题:泛型化 first(对比 any 版的差别)、带约束的 pluck(从对象数组里抽字段)、groupBy(按字段分组,GIS 统计的常见操作)
  3. 全部在 Playground 过编译 + 故意传错类型验证拦截

通关标准:

  • 能不看资料写出 <T>(items: T[]): T | undefined
  • 能说出 T extends { name: string } 里 extends 的含义
  • 认脸测试全对:给 5 个带尖括号的声明说出含义

五、自测题

  1. 函数返回 void 和返回 undefined 的类型标注有什么实际差别?
  2. 不用泛型、用 any 写 first(),调用方会失去什么?
  3. <T extends { id: number }>(xs: T[]) 的含义?
  4. Promise<QuakeCollection> 怎么读?
  5. 泛型的类型参数名必须叫 T 吗?

参考答案

  1. : void 表示"调用方不该使用返回值"(返回什么都被禁止使用);返回 undefined 的函数调用结果只能赋给 undefined 类型——两者都约束调用方,语义略不同。
  2. 调用方拿到的值类型是 any:失去补全、失去检查,undefined 的情况也不再被提醒。
  3. T 必须满足"至少有 id: number 字段"的约束,函数内可安全访问 x.id。
  4. 一个"最终会兑现为 QuakeCollection 的异步凭证"——await 它得到 QuakeCollection。
  5. 不必须,T 只是惯例(Type 的首字母);多个参数时常叫 U、V、K、V 等,语义化命名如 <TKey, TValue> 更可读。

六、下一步

类型系统会用了 → 第 23 节:TS 在 Vite 项目中,真实项目里 TS 怎么配置、报错怎么读。

TAGSweb