blog

TS 类型转换中的几个小技巧

2025-05-14

前两天在二次封装一个从第三方库导出的函数时,发现我需要这个库中的一个类型, 可是这个类型却没有导出,于是我就想能不能直接从函数里提取出来呢?

这就引出了今天要聊的内容

TypeScript 类型转换中几个很常用但容易忽略的内置工具类型:ParametersReturnTypeAwaited 等等。


一、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 的“跳转类型定义”结合看,帮助理解复杂函数的返回结构。
CD..