TS 类型转换中的几个小技巧
2025-05-14
前两天在二次封装一个从第三方库导出的函数时,发现我需要这个库中的一个类型, 可是这个类型却没有导出,于是我就想能不能直接从函数里提取出来呢?
这就引出了今天要聊的内容
TypeScript 类型转换中几个很常用但容易忽略的内置工具类型:Parameters
、ReturnType
、Awaited
等等。
一、Parameters:从函数中提取参数类型
type Options = Parameters<fetchSomething>[1]
它其实在做的事情是:
Parameters<T>
:这是 TS 的一个内置类型工具,用来提取函数T
的参数类型,返回的是一个元组类型。[1]
:从这个元组中取出第 2 个参数(下标从 0 开始)。
举个栗子:
假设你有这样一个函数:
function fetchSomething(id: string, options: { cache: boolean }) {
// ...
}
那你可以这样取出 options
的类型:
type Options = Parameters<typeof fetchSomething>[1]
// 推导出来就是:{ cache: boolean }
这个用法在什么场景下很爽?比如你写一个工具函数,要对原函数做一些封装,而你不想重复写参数类型,就可以直接复用原函数的定义。
实战场景:
我们有个接口函数:
function getUser(id: string, config: { auth: boolean }) {
// ...
}
我们封装了一个 withRetry
的函数:
function withRetry(fn: Function) {
return (...args: any[]) => {
// retry logic
return fn(...args);
};
}
但是这样写 args: any[]
太野了。怎么能让 args
的类型保持和原函数一致?
用 Parameters
一步到位:
function withRetry<T extends (...args: any) => any>(fn: T) {
return (...args: Parameters<T>) => {
// retry logic
return fn(...args);
};
}
是不是优雅多了?
二、ReturnType:获取函数的返回值类型
这个也超常用,比如你要获取某个接口调用返回的结构体,但不想再手动维护类型,就可以直接这样:
function getProfile(): Promise<{ name: string; age: number }> {
// ...
}
type Profile = ReturnType<typeof getProfile>
// Profile = Promise<{ name: string; age: number }>
不过注意:这里返回的是 Promise,而不是里面的数据。如果你想拿到里面的数据结构,就得上下一个兵器了:Awaited
。
三、Awaited:提取 Promise 的结果类型
有时候你会遇到这样一种需求 —— 想要获取 async
函数真正 resolve 出来的类型,而不是整个 Promise
包裹着的那层。
比如继续刚才的例子:
type ResolvedProfile = Awaited<ReturnType<typeof getProfile>>
// ResolvedProfile = { name: string; age: number }
超级实用!
实战场景:
我们用 axios
请求接口,封装了一个请求函数:
async function request<T>(url: string): Promise<T> {
const response = await axios.get<T>(url)
return response.data
}
然后其他地方我们写了:
const data = await request<UserProfile>('/api/profile')
那我们怎么推导 data
的类型?理想情况下:
type DataType = Awaited<ReturnType<typeof request<UserProfile>>>
// DataType = UserProfile
这样之后再做一些类型组合时就不用头疼手动复制粘贴了。
总结一下常用的 TS 类型工具组合:
工具类型 | 作用 |
---|---|
Parameters<T> | 提取函数参数组成的元组类型 |
ReturnType<T> | 提取函数返回值类型 |
Awaited<T> | 获取 Promise 中实际 resolve 的类型 |
这些类型工具在封装函数、复用已有类型、提高代码可维护性方面非常好用,配合 typeof
基本上能应对大部分场景的类型提取问题。
最后小Tips
Awaited
是 TypeScript 4.5+ 新加的,老项目可能没法用;- 如果是第三方库的函数,尽量
typeof xxx
+Parameters
/ReturnType
提取,不要重复写类型; - 可以和 IDE 的“跳转类型定义”结合看,帮助理解复杂函数的返回结构。