Utility Type 工具类型
TypeScript 提供了多种内置的工具类型(Utility Types),用于简化常见的类型转换操作。
Awaited<Type>
用于获取 Promise 的返回值类型,支持递归解包嵌套的 Promise。
type A = Awaited<Promise<string>>
// type A = string
type B = Awaited<Promise<Promise<number>>>
// type B = number
type C = Awaited<boolean | Promise<number>>
// type C = number | boolean常见应用: 在 async 函数中获取返回值的类型定义,或者从第三方库的异步 API 中提取实际返回的数据类型。
async function fetchUser(): Promise<{ name: string; age: number }> {
return { name: 'Alice', age: 30 }
}
type User = Awaited<ReturnType<typeof fetchUser>>
// type User = { name: string; age: number }Partial<Type>
将类型 Type 中的所有属性变为可选的(?)。
interface User {
name: string
age: number
email: string
}
type PartialUser = Partial<User>
// type PartialUser = {
// name?: string
// age?: number
// email?: string
// }
const update: PartialUser = { name: 'Bob' } // 只更新部分字段常见应用: 更新操作中只传入需要修改的字段;配置对象的类型定义,允许用户只提供部分配置项。
function updateUser(id: number, fields: Partial<User>) {
// 只需要传入要更新的字段
}
updateUser(1, { email: 'new@example.com' })Required<Type>
将类型 Type 中的所有属性变为必选的,与 Partial 相反。
interface Config {
host?: string
port?: number
ssl?: boolean
}
type RequiredConfig = Required<Config>
// type RequiredConfig = {
// host: string
// port: number
// ssl: boolean
// }
const config: RequiredConfig = {
host: 'localhost',
port: 443,
ssl: true,
}常见应用: 当某些配置项在特定场景下必须全部提供时;消除可选属性的不确定性。
// 比如:生产环境必须提供所有配置
function createProdConfig(config: Required<Config>) {
// config.host, config.port, config.ssl 都是必选的
}Readonly<Type>
将类型 Type 中的所有属性变为只读的。
interface Point {
x: number
y: number
}
type ReadonlyPoint = Readonly<Point>
// type ReadonlyPoint = {
// readonly x: number
// readonly y: number
// }
const p: ReadonlyPoint = { x: 10, y: 20 }
// p.x = 30 // Error: Cannot assign to 'x' because it is a read-only property.常见应用: 定义不可变数据;确保函数不会意外修改传入的对象;Redux 中的 state 类型定义。
function printPoint(point: Readonly<Point>) {
// point.x = 100 // Error,确保函数内部不会修改参数
console.log(point.x, point.y)
}
// 配合 as const 创建深度只读对象
const config = {
host: 'localhost',
port: 8080,
} as const
// config.host = 'other' // ErrorRecord<Keys, Type>
构造一个对象类型,其键的类型为 Keys,值的类型为 Type。
type Roles = 'admin' | 'user' | 'guest'
type RoleConfig = Record<Roles, { permissions: string[] }>
const config: RoleConfig = {
admin: { permissions: ['read', 'write', 'delete'] },
user: { permissions: ['read', 'write'] },
guest: { permissions: ['read'] },
}常见应用: 构建映射表(如权限映射、状态映射);将联合类型转换为键值对结构;字典/缓存对象的类型定义。
// 状态码映射
type StatusCode = 200 | 404 | 500
type StatusMessage = Record<StatusCode, string>
const messages: StatusMessage = {
200: 'OK',
404: 'Not Found',
500: 'Internal Server Error',
}
// 用 Record 构建字典
type Dict<T> = Record<string, T>
const userMap: Dict<{ name: string }> = {}
userMap['user1'] = { name: 'Alice' }Pick<Type, Keys>
从类型 Type 中选取一组属性来构造新类型。
interface User {
id: number
name: string
email: string
password: string
}
type PublicUser = Pick<User, 'id' | 'name'>
// type PublicUser = {
// id: number
// name: string
// }
const publicInfo: PublicUser = { id: 1, name: 'Alice' }常见应用: 从已有类型中提取需要的字段,适合用于 API 返回值的类型定义、DTO 对象的类型定义。
// 用户列表只展示 id 和 name
type UserListItem = Pick<User, 'id' | 'name'>
// 用户表单只需要 email 和 name
type UserForm = Pick<User, 'name' | 'email'>Omit<Type, Keys>
从类型 Type 中排除一组属性来构造新类型,与 Pick 相反。
interface User {
id: number
name: string
email: string
password: string
}
type UserWithoutPassword = Omit<User, 'password'>
// type UserWithoutPassword = {
// id: number
// name: string
// email: string
// }
// 可以安全地返回给前端(不含密码)
const safe: UserWithoutPassword = { id: 1, name: 'Alice', email: 'alice@example.com' }常见应用: 排除敏感字段(如密码)后返回给客户端;排除不需要的字段创建子类型。
// 排除多个字段
type UserSummary = Omit<User, 'password' | 'email'>
// 配合泛型使用
function sanitizeUser<T extends User>(user: T): Omit<T, 'password'> {
const { password, ...rest } = user
return rest
}Exclude<UnionType, ExcludedMembers>
从联合类型中排除某些类型。
type All = 'a' | 'b' | 'c' | 'd'
type WithoutA = Exclude<All, 'a'>
// type WithoutA = 'b' | 'c' | 'd'
type Event = 'click' | 'scroll' | 'mousemove'
type NotScroll = Exclude<Event, 'scroll'>
// type NotScroll = 'click' | 'mousemove'常见应用: 从联合类型中过滤掉不需要的成员;类型层面的集合减法操作。
// 从一组 HTTP 方法中排除不安全的
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
type SafeMethod = Exclude<HttpMethod, 'POST' | 'PUT' | 'DELETE' | 'PATCH'>
// type SafeMethod = 'GET'
// 排除 null 和 undefined(可以用 NonNullable 更简洁)
type T1 = Exclude<string | number | null | undefined, null | undefined>
// type T1 = string | numberExtract<Type, Union>
从类型 Type 中提取可以赋值给 Union 的类型,与 Exclude 相反。
type All = 'a' | 'b' | 'c' | 'd'
type OnlyAB = Extract<All, 'a' | 'b'>
// type OnlyAB = 'a' | 'b'
type Mixed = string | number | boolean | (() => void)
type Callable = Extract<Mixed, Function>
// type Callable = () => void常见应用: 从联合类型中提取满足条件的子集;提取函数类型。
// 提取特定的事件类型
type EventName = 'click' | 'scroll' | 'keydown' | 'keyup'
type KeyboardEvents = Extract<EventName, `key${string}`>
// type KeyboardEvents = 'keydown' | 'keyup'
// 从混合类型中提取数组类型
type Types = string[] | number[] | boolean
type Arrays = Extract<Types, unknown[]>
// type Arrays = string[] | number[]NonNullable<Type>
从类型 Type 中排除 null 和 undefined。
type T1 = NonNullable<string | null | undefined>
// type T1 = string
type T2 = NonNullable<number[] | null>
// type T2 = number[]
type T3 = NonNullable<null | undefined>
// type T3 = never常见应用: 过滤掉可空类型,确保值的类型安全。
function getValue(): string | null {
return Math.random() > 0.5 ? 'hello' : null
}
// 使用 filter 配合类型守卫
const values = [getValue(), getValue(), getValue()]
const nonNullValues = values.filter((v): v is NonNullable<typeof v> => v !== null)
// nonNullValues: string[]
// 表单字段的验证后类型
type FormField = string | null | undefined
type ValidField = NonNullable<FormField>
// type ValidField = stringParameters<Type>
提取函数类型的参数列表,返回一个元组类型。
declare function foo(a: string, b: number): void
type P = Parameters<typeof foo>
// type P = [a: string, b: number]
const args: P = ['hello', 42]
foo(...args)常见应用: 获取第三方函数的参数类型;编写高阶函数时复用参数类型;在 React 中提取组件 Props。
// 复用现有函数的参数类型
function logEvent(event: string, data: Record<string, unknown>) { /* ... */ }
type LogEventParams = Parameters<typeof logEvent>
// type LogEventParams = [event: string, data: Record<string, unknown>]
// 在高阶函数中使用
function wrapLog(...args: LogEventParams) {
console.log('[LOG]', args[0])
logEvent(...args)
}ConstructorParameters<Type>
提取构造函数类型的参数列表,返回一个元组或数组类型。
class Person {
constructor(public name: string, public age: number) {}
}
type CP = ConstructorParameters<typeof Person>
// type CP = [name: string, age: number]
const params: CP = ['Alice', 30]
const person = new Person(...params)常见应用: 在工厂函数或依赖注入中复用构造函数参数类型。
// 抽象工厂模式
abstract class Animal {
abstract makeSound(): void
}
class Dog extends Animal {
constructor(public breed: string) { super() }
makeSound() { console.log('Woof') }
}
class Cat extends Animal {
constructor(public color: string) { super() }
makeSound() { console.log('Meow') }
}
type AnimalConstructor<T extends Animal> = new (...args: any[]) => T
function createAnimal<T extends Animal>(
ctor: AnimalConstructor<T>,
...args: ConstructorParameters<AnimalConstructor<T>>
): T {
return new ctor(...args)
}
const dog = createAnimal(Dog, 'Golden Retriever')ReturnType<Type>
提取函数类型的返回值类型。
declare function createUser(name: string): { id: number; name: string }
type R = ReturnType<typeof createUser>
// type R = { id: number; name: string }
const user: R = { id: 1, name: 'Alice' }常见应用: 获取函数返回值类型,避免重复定义;在 Redux 中推导 action 类型。
// 从工厂函数推导返回类型
function createCounter() {
return {
count: 0,
increment() { this.count++ },
decrement() { this.count-- },
}
}
type Counter = ReturnType<typeof createCounter>
// 在 async 函数中使用
async function fetchData(): Promise<{ items: string[]; total: number }> {
return { items: ['a', 'b'], total: 2 }
}
type DataResponse = Awaited<ReturnType<typeof fetchData>>
// type DataResponse = { items: string[]; total: number }InstanceType<Type>
提取构造函数类型的实例类型。
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
type P = InstanceType<typeof Person>
// type P = Person
const p: P = new Person('Alice', 30)常见应用: 在工厂函数或依赖注入容器中获取构造函数创建的实例类型。
// 依赖注入容器
class Container {
private services = new Map<new (...args: any[]) => any, any>()
register<T extends new (...args: any[]) => any>(ctor: T, instance: InstanceType<T>) {
this.services.set(ctor, instance)
}
resolve<T extends new (...args: any[]) => any>(ctor: T): InstanceType<T> {
return this.services.get(ctor)
}
}
const container = new Container()
container.register(Person, new Person('Alice', 30))
const resolved = container.resolve(Person) // type: PersonNoInfer<Type>
阻止 TypeScript 从泛型参数中进行类型推断,强制使用显式声明的类型。(TypeScript 5.4+)
function createList<T>(items: T[], defaultValue: NoInfer<T>): T[] {
return items.length > 0 ? items : [defaultValue]
}
// createList([1, 2], 'default')
// Error: 'default' 是 string,不会被推断为 string | number
// 必须使用与 T 一致的类型
createList([1, 2], 0) // OK常见应用: 当有多个参数属于同一泛型类型,但希望某些参数不参与类型推断时使用。
// 限制 defaultValue 不能影响 items 的类型推断
function fetchItems<T>(
url: string,
fallback: NoInfer<T>
): Promise<T> {
return fetch(url).then(res => res.json()).catch(() => Promise.resolve(fallback))
}
// 类型推断完全由 url/API 决定,fallback 不会扩大类型
declare function apiGetUser(): Promise<{ id: number; name: string }>ThisParameterType<Type>
提取函数类型中 this 参数的类型。
function getAge(this: { age: number }) {
return this.age
}
type ThisType = ThisParameterType<typeof getAge>
// type ThisType = { age: number }常见应用: 在需要提取方法所属对象类型的高级类型编程中使用。
class Component {
state = { count: 0 }
render(this: Component) {
return this.state.count
}
}
type CompThis = ThisParameterType<typeof Component.prototype.render>
// type CompThis = ComponentOmitThisParameter<Type>
从函数类型中移除 this 参数,返回不带 this 类型的函数类型。
function greet(this: { name: string }, greeting: string) {
return `${greeting}, ${this.name}`
}
type PlainGreet = OmitThisParameter<typeof greet>
// type PlainGreet = (greeting: string) => string
const greetFn: PlainGreet = greet.bind({ name: 'World' })
greetFn('Hello') // 'Hello, World'常见应用: 将带有 this 类型的方法转换为普通函数类型,方便作为回调传递。
class Handler {
data = 'important'
handle(this: Handler, event: string) {
console.log(event, this.data)
}
}
// 直接传递方法会导致 this 丢失
// onClick(handler.handle) // Error: 'this' 类型不匹配
// 使用 OmitThisParameter 获取普通函数签名
type HandleFn = OmitThisParameter<typeof Handler.prototype.handle>
// type HandleFn = (event: string) => void
const boundHandle: HandleFn = new Handler().handle.bind(new Handler())ThisType<Type>
用于标记上下文中的 this 类型,不返回转换后的类型。通常配合对象字面量使用。
type ObjectDescriptor<D, M> = {
data?: D
methods?: M & ThisType<D & M> // methods 中的 this 类型是 D & M
}
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
const data = desc.data || {} as D
const methods = desc.methods || {} as M
return { ...data, ...methods } as D & M
}
const obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx // this 的类型是 { x: number; y: number } & { moveBy: ... }
this.y += dy
},
},
})常见应用: Vue Options API 中 methods 里的 this 能够访问 data 中的属性,就是借助了 ThisType。
// 模拟 Vue 2.x 的 Options API 类型推导
interface VueOptions<D, M> {
data(this: void): D
methods: M & ThisType<D & M>
}
declare function defineComponent<D, M>(options: VueOptions<D, M>): D & M
const vm = defineComponent({
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++ // this 能访问 data 中的 count
},
},
})Intrinsic String Manipulation Types 内置字符串操作类型
TypeScript 4.1 引入了基于模板字面量类型的内置字符串操作类型,这些类型在编译器中直接实现。
Uppercase<StringType>
将字符串字面量类型的每个字符转换为大写。
type Greeting = 'Hello, World'
type ShoutingGreeting = Uppercase<Greeting>
// type ShoutingGreeting = 'HELLO, WORLD'
type Status = 'success' | 'error'
type UpperStatus = Uppercase<Status>
// type UpperStatus = 'SUCCESS' | 'ERROR'常见应用: 将枚举风格的字符串联合类型转换为大写形式,统一格式。
Lowercase<StringType>
将字符串字面量类型的每个字符转换为小写。
type Greeting = 'Hello, World'
type QuietGreeting = Lowercase<Greeting>
// type QuietGreeting = 'hello, world'
type Method = 'GET' | 'POST' | 'DELETE'
type LowerMethod = Lowercase<Method>
// type LowerMethod = 'get' | 'post' | 'delete'常见应用: 将 HTTP 方法或其他标识符统一转换为小写。
Capitalize<StringType>
将字符串字面量类型的首字母转换为大写。
type Method = 'get' | 'post' | 'delete'
type CapitalizedMethod = Capitalize<Method>
// type CapitalizedMethod = 'Get' | 'Post' | 'Delete'
type EventName = 'click'
type HandlerName = `on${Capitalize<EventName>}`
// type HandlerName = 'onClick'常见应用: 动态生成符合命名规范的类型,比如从事件名生成 handler 名。
type Events = 'click' | 'change' | 'submit'
type Handlers = {
[K in Events as `on${Capitalize<K>}`]: (event: K) => void
}
// type Handlers = {
// onClick: (event: 'click') => void
// onChange: (event: 'change') => void
// onSubmit: (event: 'submit') => void
// }Uncapitalize<StringType>
将字符串字面量类型的首字母转换为小写。
type Method = 'GET' | 'POST' | 'DELETE'
type UncapitalizedMethod = Uncapitalize<Method>
// type UncapitalizedMethod = 'gET' | 'pOST' | 'dELETE'
// 更实用的场景:将类名转换为实例变量名
type ClassName = 'UserService'
type InstanceName = Uncapitalize<ClassName>
// type InstanceName = 'userService'常见应用: 从类名或大写开头的标识符生成小写开头的变量名。
type Services = 'UserService' | 'OrderService' | 'PaymentService'
type ServiceInstances = {
[K in Services as Uncapitalize<K>]: K
}
// type ServiceInstances = {
// userService: 'UserService'
// orderService: 'OrderService'
// paymentService: 'PaymentService'
// }总结
| 工具类型 | 用途 |
|---|---|
Awaited<T> | 解包 Promise,获取异步返回值类型 |
Partial<T> | 所有属性变为可选 |
Required<T> | 所有属性变为必选 |
Readonly<T> | 所有属性变为只读 |
Record<K, T> | 构造键为 K、值为 T 的对象类型 |
Pick<T, K> | 从 T 中选取属性 K 构造新类型 |
Omit<T, K> | 从 T 中排除属性 K 构造新类型 |
Exclude<U, E> | 从联合类型 U 中排除 E |
Extract<T, U> | 从类型 T 中提取可赋值给 U 的部分 |
NonNullable<T> | 排除 null 和 undefined |
Parameters<T> | 提取函数参数类型元组 |
ConstructorParameters<T> | 提取构造函数参数类型元组 |
ReturnType<T> | 提取函数返回值类型 |
InstanceType<T> | 提取构造函数实例类型 |
NoInfer<T> | 阻止类型推断 |
ThisParameterType<T> | 提取函数 this 参数类型 |
OmitThisParameter<T> | 移除函数 this 参数类型 |
ThisType<T> | 标记上下文中的 this 类型 |
Uppercase<S> | 字符串转大写 |
Lowercase<S> | 字符串转小写 |
Capitalize<S> | 字符串首字母大写 |
Uncapitalize<S> | 字符串首字母小写 |