typescript的高级类型
TypeScript 重点速记(7 个模块)
0. 心法(先建立直觉)
- 类型 = 集合:
A | B并集,A & B交集,never空集,unknown全集,any关闭检查。 - TS 用的是结构化类型(structural typing):只看形状,不看名字。
- 类型体操 = 三板斧:条件类型(分支)+ 映射(遍历)+ infer(提取)。
1. 高级类型
// 联合 + 字面量
type Status = 'todo' | 'done' | 'archived'
type Id = string | number
// 判别式联合(实战最常用,配合 switch 自动收窄)
type Shape =
| { kind: 'circle'; r: number }
| { kind: 'rect'; w: number; h: number }
function area(s: Shape) {
switch (s.kind) { // 靠 kind 收窄
case 'circle': return Math.PI * s.r ** 2 // 此处 s 已是 circle
case 'rect': return s.w * s.h
}
}
类型收窄手段:typeof / in / instanceof / 判别式字段 / 自定义类型守卫 function isX(v: unknown): v is X。
条件类型 + infer
type IsStr<T> = T extends string ? true : false
type Item<T> = T extends Array<infer U> ? U : T // infer 提取
type Ret<F> = F extends (...a: any[]) => infer R ? R : never
// 分发特性:裸类型参数遇联合会分发
type A = Item<string[] | number> // string | number
// 阻止分发:[T] extends [U] ? X : Y
常用内置工具(背下来)
Partial / Required / Readonly / Pick / Omit / Record / Exclude / Extract / NonNullable / ReturnType / Parameters / Awaited
2. class 类
class Person {
public name: string // public 可省
private secret = 1 // 仅本类
protected age = 18 // 本类 + 子类
readonly id: string // 只读
static count = 0 // 静态,属于类本身
constructor(name: string, public email: string) { // 参数属性:一行声明并赋值
this.name = name
}
get upper() { return this.name.toUpperCase() } // getter
}
关键区分:
Person= 实例类型(描述new出来的对象)typeof Person= 构造器类型(描述类本身,含 static 成员)
function create(Ctor: new (name: string) => Person) { return new Ctor('a') }
// 或:typeof Person
其他:abstract 抽象类/方法(不能直接 new,子类必须实现);implements 实现接口(只约束形状,不继承实现);类可以同时 extends 一个类并 implements 多个接口。
3. 类型兼容性(最容易踩坑)
对象:属性多 → 赋给 属性少,OK(多的被忽略);反之不行。
type A = { a: number }
type B = { a: number; b: string }
let b: B = { a: 1, b: 'x' }
let a: A = b // ✅
// b = a // ❌ 缺 b
⚠️ 多余属性检查:只有对象字面量直接赋值才报错,变量中转不报错。
let a2: A = { a: 1, b: 'x' } // ❌ 字面量多属性
let tmp = { a: 1, b: 'x' }; let a3: A = tmp // ✅
绕开:类型断言 as A 或加索引签名。
函数:
- 参数个数:少的可以赋给多的(少参数函数能安全替代)。
- 返回值:协变(子类型返回值 OK)。
- 参数类型:
strictFunctionTypes: true下逆变(参数更宽才安全);方法(method 简写)是双变。
type F1 = (x: string | number) => void
type F2 = (x: string) => void
let f1: F1 = (x) => {}
let f2: F2 = f1 // ✅ 参数更宽 → 更窄
其它:private/protected 成员存在时,只有同一声明来源的类才兼容;枚举与 number 不互相兼容(数字枚举除外)。
4. 交叉类型 &
type A = { a: string }
type B = { b: number }
type C = A & B // { a: string; b: number },必须同时满足
- 对象交叉 = 属性合并;同名属性会再取交集:
{x: string} & {x: number}→x: string & number= never(易错!) - 联合 + 交叉有分配律:
(A|B) & C = (A&C)|(B&C) - interface extends vs type &:
extends同名成员冲突直接报错;&不报错而是变成交集(可能静默变 never)。
5. 泛型 + keyof
function identity<T>(v: T): T { return v }
// 约束 & 默认
function getProp<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key] // 键安全 + 返回值类型精确
}
getProp({ a: 1, b: 'x' }, 'a') // number
// getProp({ a: 1 }, 'c') // ❌ 编译期报错
keyof 要点:
interface P { name: string; age: number }
type Keys = keyof P // 'name' | 'age'
type Keys2 = keyof P & string // 只要字符串键
keyof any // string | number | symbol
keyof [] // number | 'length' | 'push' | ...
泛型常见位置:函数、接口 interface Api<T>、类型别名 type Res<T> = { data: T }、类 class Repo<T>、约束 extends、默认 <T = string>。
6. 索引签名类型 + 索引查询类型
// 索引签名:描述"未知键"的对象
interface Dict { [key: string]: number }
const d: Dict = { a: 1, b: 2 }
interface Nums { [index: number]: string } // 数组式
规则/坑:
- 一旦写了
[key: string]: X,所有具名属性的类型都必须是 X 的子类型(否则报"属性不兼容索引签名")。 Record<K, T>就是索引签名的糖:Record<'a'|'b', number> = { a: number; b: number }。- js 中对象键最终都是 string/数字,用
Record<string, unknown>比object/{}安全({}几乎不校验)。
// 索引查询(访问)类型
type NameType = P['name'] // string
type Vals = P[keyof P] // string | number
type Arr = string[]
type Elem = Arr[number] // string ← 数组元素惯用写法
7. 映射类型(类型体操主力)
type Mapped<T> = { [K in keyof T]: T[K] } // 遍历键,原样复制
修饰符操作:
type Partial2<T> = { [K in keyof T]?: T[K] } // +? 可选
type Required2<T> = { [K in keyof T]-?: T[K] } // -? 去可选
type Readonly2<T> = { readonly [K in keyof T]: T[K] }
type Mutable<T> = { -readonly [K in keyof T]: T[K] } // 去只读
键重映射(TS 4.1+,as)
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] }
type FilterStr<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K] }
手写核心工具(理解一次就通)
type Pick2<T, K extends keyof T> = { [P in K]: T[P] }
type Omit2<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
type Record2<K extends keyof any, V> = { [P in K]: V }
type Exclude2<T, U> = T extends U ? never : T
速记口诀
- 收窄:判别式 +
in/typeof+v is T。 - 兼容:对象"多给少"OK,字面量多属性报错,函数参数宽给窄 OK。
- 遍历:
[K in keyof T],+?/-?、readonly/-readonly、as改名/过滤。 - 提取:条件类型里用
infer;T['k']、Arr[number]、T[keyof T]。 - 冲突:交叉同名属性 →
never;索引签名要求所有属性兼容它。

浙公网安备 33010602011771号