第 14 章:高级类型
学习目标
- 学会条件类型 T extends U ? X : Y
- 掌握映射类型与 key 重映射
- 理解 infer 关键字与模板字面量类型
- 实战几个常用高级类型
一、条件类型(Conditional Types)
语法:T extends U ? X : Y
typescript
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实用:Nullable
type Nullable<T> = T extends null | undefined ? never : T;
type T = Nullable<string | null>; // string二、分布式条件类型(Distributive)
联合类型会被拆分逐个判断:
typescript
type ToArray<T> = T extends unknown ? T[] : never;
type A = ToArray<string | number>; // string[] | number[]⚠️ 坑 1:用
[T] extends ...包裹可以阻止拆分:
typescript
type ToArrayNonDist<T> = [T] extends unknown ? T[] : never;
type B = ToArrayNonDist<string | number>; // (string | number)[]三、infer 关键字(类型推断)
typescript
// 提取函数返回值
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = MyReturnType<() => string>; // string
// 提取 Promise 内层类型
type PromiseType<T> = T extends Promise<infer U> ? U : never;
type B = PromiseType<Promise<number>>; // number
// 提取数组元素
type ElementOf<T> = T extends (infer E)[] ? E : never;
type C = ElementOf<string[]>; // string四、映射类型(Mapped Types)
typescript
type Flags<T> = {
[K in keyof T]: boolean;
};
interface User { id: number; name: string }
type UserFlags = Flags<User>;
// { id: boolean; name: boolean }修饰符映射:
typescript
// 全部可选
type MyPartial<T> = { [K in keyof T]?: T[K] };
// 全部只读
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
// 去掉 readonly
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
// 去掉可选
type Required<T> = { [K in keyof T]-?: T[K] };⚠️ 坑 2:
+/-前缀要写在修饰符前面,readonly/?关键字前面。
五、as 重映射(TS 4.1+)
typescript
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface User {
id: number;
name: string;
}
type UserGetters = Getters<User>;
// {
// getId: () => number;
// getName: () => string;
// }六、模板字面量类型(Template Literal Types)
typescript
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'
type HoverEvent = EventName<'hover'>; // 'onHover'
// 配合联合
type AllEvents = EventName<'click' | 'hover' | 'focus'>;
// 'onClick' | 'onHover' | 'onFocus'内置字符串工具:
typescript
Uppercase<'hello'>; // 'HELLO'
Lowercase<'HELLO'>; // 'hello'
Capitalize<'hello'>; // 'Hello'
Uncapitalize<'Hello'>; // 'hello'七、递归类型(实战)
typescript
// JSON 类型
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };
const data: Json = {
name: 'Tom',
age: 18,
tags: ['admin', 'user'],
meta: { active: true },
};
// 深度 Readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};八、实战:DeepPartial
typescript
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
interface Config {
server: {
host: string;
port: number;
ssl: { cert: string };
};
}
const cfg: DeepPartial<Config> = {
server: {
port: 8080, // host 可省
ssl: {}, // cert 可省
},
};九、实战:类型安全的事件系统
typescript
interface Events {
login: { userId: string };
logout: { reason: string };
click: { x: number; y: number };
}
type EventName = keyof Events;
type EventPayload<T extends EventName> = Events[T];
class Emitter {
emit<T extends EventName>(event: T, payload: EventPayload<T>): void {
console.log(`${event}:`, payload);
}
}
const e = new Emitter();
e.emit('login', { userId: '1' }); // ✅
e.emit('click', { x: 1, y: 2 }); // ✅
e.emit('login', { reason: 'x' }); // ❌ 字段不对⚠️ 坑 3:高级类型别玩过头,代码可读性下降。团队项目里建议加 ESLint 规则限制复杂度(如
no-restricted-syntax)。
十、本章小结
| 类型 | 关键 |
|---|---|
| 条件类型 | T extends U ? X : Y,分布式会自动拆分联合 |
| infer | T extends Promise<infer U>,提取/解构 |
| 映射类型 | { [K in keyof T]: ... } |
| 修饰符 | readonly / ?,前面加 - 去掉 |
| as 重映射 | as NewKey 改 key 名 |
| 模板字面量 | 反引号 + ${} 拼字符串字面量 |
| 内置 | Uppercase / Lowercase / Capitalize / Uncapitalize |
动手练习
- 条件类型:写
IsNever<T> = [T] extends [never] ? true : false(注意用[T]防止分布式) - infer 实战:实现
FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never - 模板字面量:用模板字面量生成
onClick/onHover/onFocus三种事件名联合
下一章:第 15 章:模块与命名空间 →