类型体操速通(二):热身操

426 阅读2分钟

哈喽!最近要赶uni-app迭代项目的需求,一直没空更,今天周末终于有空了。上回梳理了一下掌握TS类型体操的必备知识,这里再强调一下体操最常用到的extendsinfer关键字,是必须掌握的。那么现在我们来做个热身操哈~

先上第一个需求~

这个需求很简单,实现类型 Awaited,比如从 Promise<ToDoType> 拿到 ToDoType

这里提一下,不要一看到Promise对象就习惯性想到要用ES8 的 async 函数,执行 await 拿到Promise对象,因为TS永远不会去执行代码

需求仅仅是要从 Promise< T > 提取出泛型 T

基于业务经验,我们一般会想到用 infer 来解决

type MyAwaited<T> = T extends Promise<infer U> ? U : never

但仅仅是这样的话,我们就没有考虑到Promise一些嵌套回调的场景,类似下面这样的

Promise.resolve().then(res=>{
 console.log('1')
    return new Promise((resolve,reject)=>{
       setTimeout(()=>{
           console.log('2')
           resolve('newPromise')
       },1000) 
    }).then(res=>{
        console.log('3')
        return 'newPromise1'
    })
}).then(res=>{
   console.log('4',res)
})

考虑到这里,就很自然地会和递归联系到了一起,所以完成这个需求的标准答案是

// 标准答案考虑了嵌套 Promise 的场景,采用用递归的写法
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer P>
  ? P extends Promise<unknown> ? MyAwaited<P> : P
  : never

这里附上原题链接: github.com/type-challe…

是不是有点不够,再看下第二个需求~

实现类型 FirstItemLastItem , FirstItem< T >作用是获取元组第一项的类型,LastItem< T >作用是获取元组最后一项的类型

//demo
type arr1=[1,2,3]
type arr2=['南山','橘子','一泽']

//ToDo
type res1 = FirstItem<arr1> //   1
type res2 = FirstItem<arr2> //  '南山'
type res3 = LastItem<arr1> //   3
type res4 = LastItem<arr2> //  '一泽'

我们都知道ES6的数组解构/展开 ,TS里也可以用的,所以结合infer,答案是

 // 实现FirstItem
 type FirstItem<T extends unknown[]> = T extends [infer P, ...infer _ ] ? P : never;
 
 // 实现LastItem
 type LastItem<T extends unknown[]> = T extends [...infer _ , infer P] ? P : never;

特别说明下

  • 这里只需要一个泛型,不需要再使用另一个新泛型表示其他元素的类型,所以用 _
  • 不能用 ...Rest 代替 ...infer P/_ 因为TS里不能随便使用一个未定义的泛型

今天的热身操就到这了,刷力扣去啦~