前置要求:TypeScript基础
学习时长:约2-3天
适用场景:大型项目、类型安全、库开发
一、高级类型
1.1 联合类型与交叉类型
// 联合类型:可以是多种类型之一
type StringOrNumber = string | number
type Status = 'active' | 'inactive' | 'banned'
// 交叉类型:合并多个类型
type UserWithPost = User & Post
type AdminUser = User & { role: 'admin'; permissions: string[] }
// 使用
function format(value: StringOrNumber): string {
if (typeof value === 'string') {
return value.toUpperCase()
}
return value.toFixed(2)
}
1.2 类型守卫
// typeof守卫
function isString(value: unknown): value is string {
return typeof value === 'string'
}
// instanceof守卫
function isError(value: unknown): value is Error {
return value instanceof Error
}
// 自定义类型守卫
interface Cat { meow(): void }
interface Dog { bark(): void }
function isCat(animal: Cat | Dog): animal is Cat {
return (animal as Cat).meow !== undefined
}
// 使用
function makeSound(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow() // TypeScript知道这是Cat
} else {
animal.bark() // TypeScript知道这是Dog
}
}
1.3 可辨识联合
// 通过共同字段区分类型
interface Circle {
kind: 'circle'
radius: number
}
interface Rectangle {
kind: 'rectangle'
width: number
height: number
}
interface Triangle {
kind: 'triangle'
base: number
height: number
}
type Shape = Circle | Rectangle | Triangle
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rectangle':
return shape.width * shape.height
case 'triangle':
return (shape.base * shape.height) / 2
}
}
二、泛型高级
2.1 泛型约束
// 约束泛型必须有某些属性
interface HasId {
id: number
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id)
}
// 约束keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const user = { name: '张三', age: 25 }
getProperty(user, 'name') // OK
// getProperty(user, 'email') // Error
2.2 条件类型
// 基本条件类型
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never
type Result = ToArray<string | number> // string[] | number[]
// infer关键字
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type UnpackPromise<T> = T extends Promise<infer U> ? U : T
type A = ReturnType<() => string> // string
type B = UnpackPromise<Promise<number>> // number
type C = UnpackPromise<string> // string
2.3 映射类型
// 将所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 将所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
// 选择部分属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
// 排除部分属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
// 使用
interface User {
id: number
name: string
email: string
password: string
}
type UserPreview = Pick<User, 'id' | 'name'>
type UserWithoutPassword = Omit<User, 'password'>
type PartialUser = Partial<User>
type ReadonlyUser = Readonly<User>
三、模板字面量类型
// 基本用法
type Color = 'red' | 'blue' | 'green'
type Size = 'sm' | 'md' | 'lg'
type ColorSize = `${Color}-${Size}`
// "red-sm" | "red-md" | "red-lg" | "blue-sm" | ...
// 事件名
type EventName<T extends string> = `on${Capitalize<T>}`
type ClickEvent = EventName<'click'> // "onClick"
type MouseEvent = EventName<'mouse'> // "onMouse"
// CSS属性
type CSSProperty = `margin-${'top' | 'right' | 'bottom' | 'left'}`
// "margin-top" | "margin-right" | "margin-bottom" | "margin-left"
四、实用工具类型
// Required:所有属性变为必需
type Required<T> = {
[P in keyof T]-?: T[P]
}
// Record:构造键值对类型
type Record<K extends keyof any, T> = {
[P in K]: T
}
// Exclude:从联合类型中排除
type Exclude<T, U> = T extends U ? never : T
// Extract:从联合类型中提取
type Extract<T, U> = T extends U ? T : never
// NonNullable:排除null和undefined
type NonNullable<T> = T extends null | undefined ? never : T
// Parameters:获取函数参数类型
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never
// ConstructorParameters:获取构造函数参数类型
type ConstructorParameters<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: infer P) => any ? P : never
// InstanceType:获取实例类型
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R ? R : any
五、类型体操练习
5.1 实现DeepReadonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
interface Config {
db: {
host: string
port: number
}
cache: {
ttl: number
}
}
type ReadonlyConfig = DeepReadonly<Config>
// {
// readonly db: {
// readonly host: string
// readonly port: number
// }
// readonly cache: {
// readonly ttl: number
// }
// }
5.2 实现PartialByKeys
type PartialByKeys<T, K extends keyof T> =
Omit<T, K> & Partial<Pick<T, K>>
interface User {
id: number
name: string
email: string
}
type CreateUser = PartialByKeys<User, 'id'>
// { id?: number; name: string; email: string }
5.3 实现RequiredByKeys
type RequiredByKeys<T, K extends keyof T> =
Omit<T, K> & Required<Pick<T, K>>
interface User {
id?: number
name?: string
email?: string
}
type RequiredUser = RequiredByKeys<User, 'id' | 'name'>
// { id: number; name: number; email?: string }
六、React + TypeScript 高级
6.1 泛型组件
// 泛型列表组件
interface ListProps<T> {
items: T[]
renderItem: (item: T) => React.ReactNode
keyExtractor: (item: T) => string
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map(item => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
)
}
// 使用
<List
items={users}
renderItem={user => <span>{user.name}</span>}
keyExtractor={user => String(user.id)}
/>
6.2 泛型Hook
// 泛型useFetch
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(err => setError(err.message))
.finally(() => setLoading(false))
}, [url])
return { data, loading, error }
}
// 使用
const { data: users } = useFetch<User[]>('/api/users')
学习建议
- 先掌握基础类型,再学高级类型
- 理解泛型的本质,类型参数化
- 练习类型体操,提升类型编程能力
- 在实际项目中应用,特别是库开发
- 不要过度类型化,简单的地方让TS推断