第 11 章:类型推导
学习目标
- 理解 TS 的"上下文类型推导"
- 学会利用推导减少显式注解
- 掌握常见场景的最佳类型推导
- 避开
any蔓延导致的推导失败
一、最佳公共类型推导
typescript
// 多元素数组,推导为联合
const arr = [1, 'Tom', true]; // (string | number | boolean)[]
// 指定类型可优化
const arr2: (number | string)[] = [1, 'Tom'];二、上下文类型(Contextual Typing)
typescript
// window.onmousedown 的类型由 onmousedown 的签名推导
window.onmousedown = (e) => {
console.log(e.button); // ✅ e 已被推导为 MouseEvent
};
// 函数参数
type F = (x: number) => string;
const f: F = (x) => x.toString(); // x 推导为 number⚠️ 坑 1:箭头函数赋给有类型的变量时,参数类型会自动推导;但写成
function不会。
typescript
type F = (x: number) => void;
const f1: F = (x) => console.log(x); // ✅ x: number
const f2: F = function (x) { console.log(x); }; // ✅ x: number三、字面量类型推导
typescript
// let 推导为宽类型
let x = 'tom'; // string
x = 'jerry'; // ✅
// const 推导为字面量类型
const y = 'tom'; // 'tom'(字面量)为什么? const 不可变,所以 TS 把它推导为最精确的字面量。
四、类型拓宽 vs 类型缩小
typescript
// 类型拓宽
let a = 'hello'; // string(可重新赋任意字符串)
const b = 'hello'; // 'hello'(字面量)
// 对象
const obj = { x: 1 }; // { x: number }
obj.x = 99; // ✅
// 数组字面量
const arr = [1, 2]; // number[]五、常见推导场景
函数返回值
typescript
function add(a: number, b: number) {
return a + b; // 推断 number
}解构
typescript
const user = { id: 1, name: 'Tom' };
const { id, name } = user; // id: number, name: stringasync 函数
typescript
async function fetchUser(): Promise<User> {
const res = await fetch('/api/user');
return res.json(); // ⚠️ 实际是 Promise<any>
}⚠️ 坑 2:不加 schema 校验,
res.json()永远是Promise<any>,需要在外部校验(用 zod / io-ts / yup)。
六、控制流推导
typescript
function process(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // ✅ 收窄为 string
}
return value.toFixed(2); // ✅ 收窄为 number
}赋值与重新收窄:
typescript
let x: string | number = 'tom';
if (Math.random() > 0.5) {
x = 123; // x 变为 number
}
x.toUpperCase(); // ❌ x 可能是 number七、显式标注的反例
不要过度标注,推导已经够用:
typescript
// ❌ 多余
const num: number = 5;
const arr: number[] = [1, 2, 3];
// ✅ 利用推导
const num = 5;
const arr = [1, 2, 3];八、ReturnType / Parameters 工具类型
typescript
function getUser() {
return { id: 1, name: 'Tom' };
}
type R = ReturnType<typeof getUser>; // { id: number; name: string }
type P = Parameters<typeof getUser>; // []⚠️ 坑 3:
typeof取的是值的类型,所以typeof getUser拿到的是函数类型,再用ReturnType拆返回值。
九、本章小结
| 要点 | 关键 |
|---|---|
| 最佳公共类型 | 数组元素类型合并为联合 |
| 上下文类型 | 函数表达式参数由被赋值位置推导 |
| 字面量推导 | const 推到字面量,let 推到宽类型 |
| 控制流 | typeof / in / instanceof 收窄类型 |
| 显式标注 | 只在必要地方写(参数、无初值、联合) |
| ReturnType | 从函数推导返回值类型 |
动手练习
- 推导 vs 标注:
const arr = [1, 2, 3]的类型是什么?改const arr: (number | string)[]后呢? - 上下文类型:写一个事件回调
(e: MouseEvent) => void,看看参数能不能省略类型 - ReturnType 实战:用
ReturnType<typeof fetch>取出 fetch 的返回类型
下一章:第 12 章:类型守卫 →