TypeScript 高级类型技巧
TypeScript 的类型系统远比想象中强大。除了基础的类型注解,它还提供了丰富的高级类型特性,能帮助我们编写更安全、更智能的代码。本文将介绍一些实用的高级类型技巧,助你从 TS 新手进阶为类型体操高手。
一、泛型:类型的函数
泛型让我们编写可复用的类型安全代码:
// 基础泛型函数
function identity<T>(arg: T): T {
return arg
}
const num = identity(42) // 类型推断为 number
const str = identity('hello') // 类型推断为 string
// 泛型约束
interface HasLength {
length: number
}
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length)
}
logLength('hello') // ✅ 字符串有 length
logLength([1, 2, 3]) // ✅ 数组有 length
logLength(123) // ❌ 数字没有 length
二、条件类型
条件类型让类型系统具备"逻辑判断"能力:
// 基本语法:T extends U ? X : Y
type IsString<T> = T extends string ? 'yes' : 'no'
type A = IsString<string> // 'yes'
type B = IsString<number> // 'no'
// 实用示例:提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function greet() {
return { message: 'Hello', code: 200 }
}
type GreetReturn = ReturnType<typeof greet>
// { message: string; code: number }
三、infer 关键字
infer 用于在条件类型中提取类型:
// 提取数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never
type NumArr = ArrayElement<number[]> // number
type StrArr = ArrayElement<string[]> // string
// 提取 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
type P1 = UnwrapPromise<Promise<string>> // string
type P2 = UnwrapPromise<number> // number
// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never
function add(a: number, b: string) { return a + b }
type AddParams = Parameters<typeof add> // [number, string]
四、映射类型
映射类型可以基于已有类型创建新类型:
interface User {
id: number
name: string
email: string
}
// 所有属性变为可选
type Partial<T> = {
[K in keyof T]?: T[K]
}
type PartialUser = Partial<User>
// { id?: number; name?: string; email?: string }
// 所有属性变为只读
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
// 挑选指定属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type UserBasic = Pick<User, 'id' | 'name'>
// { id: number; name: string }
// 排除指定属性
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
type UserWithoutEmail = Omit<User, 'email'>
// { id: number; name: string }
五、模板字面量类型
TypeScript 4.1 引入的强大特性:
// 基础用法
type Greeting = `Hello, ${string}!`
const g1: Greeting = 'Hello, World!' // ✅
const g2: Greeting = 'Hi, World!' // ❌
// 组合生成类型
type Direction = 'top' | 'right' | 'bottom' | 'left'
type Size = 'sm' | 'md' | 'lg'
type Margin = `margin-${Direction}-${Size}`
// 'margin-top-sm' | 'margin-top-md' | ... 共 12 种
// 实用:CSS-in-JS 属性名转换
type CamelToKebab<S extends string> =
S extends `${infer Head}${infer Tail}`
? Tail extends Uncapitalize<Tail>
? `${Lowercase<Head>}${CamelToKebab<Tail>}`
: `${Lowercase<Head>}-${CamelToKebab<Tail>}`
: S
type Test = CamelToKebab<'backgroundColor'> // 'background-color'
六、类型守卫与收窄
// 使用 typeof
function process(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase() // 这里 value 是 string
}
return value.toFixed(2) // 这里 value 是 number
}
// 使用 in 操作符
interface Dog { bark(): void }
interface Cat { meow(): void }
function speak(animal: Dog | Cat) {
if ('bark' in animal) {
animal.bark() // Dog
} else {
animal.meow() // Cat
}
}
// 自定义类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string'
}
function demo(value: unknown) {
if (isString(value)) {
console.log(value.length) // 安全访问 string 方法
}
}
七、实用工具类型
// Record:构建对象类型
type PageInfo = { title: string }
type Pages = Record<'home' | 'about' | 'contact', PageInfo>
// { home: PageInfo; about: PageInfo; contact: PageInfo }
// Extract & Exclude:联合类型操作
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'f'> // 'a'
type T2 = Exclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
// NonNullable:排除 null 和 undefined
type T3 = NonNullable<string | null | undefined> // string
// Required:所有属性变为必选
interface Config {
host?: string
port?: number
}
type RequiredConfig = Required<Config>
// { host: string; port: number }
八、实战示例:API 响应类型
// 定义通用 API 响应结构
type ApiResponse<T> =
| { success: true; data: T; error: null }
| { success: false; data: null; error: string }
// 使用
async function fetchUser(id: number): Promise<ApiResponse<User>> {
try {
const res = await fetch(`/api/users/${id}`)
const data = await res.json()
return { success: true, data, error: null }
} catch (e) {
return { success: false, data: null, error: (e as Error).message }
}
}
// 调用时自动收窄
const result = await fetchUser(1)
if (result.success) {
console.log(result.data.name) // ✅ data 一定存在
} else {
console.log(result.error) // ✅ error 一定存在
}
九、学习资源
- TypeScript 官方手册:typescriptlang.org
- Type Challenges:类型体操练习
- Matt Pocock 教程:Total TypeScript
掌握这些高级类型技巧,你将能够编写更健壮的代码,在编译时就捕获潜在错误。类型系统不是束缚,而是最可靠的"文档"和"测试"。继续探索 TypeScript 的类型魔法吧!