likes
comments
collection
share

typescript 类型体操 之 189-easy-awaited

作者站长头像
站长
· 阅读数 56

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,本片文章带大家做其中简单难度的第十道题 189-easy-awaited

下面这个是类型体操github仓库:

type-challenges/type-challenges: Collection of TypeScript type challenges with online judge (github.com)

189-easy-awaited

我们首先还是先通过题目的README来看一下题目的要求

typescript 类型体操 之 189-easy-awaited

看完 README 看似这道题好像非常的简单,用我们之前介绍过的 infer 关键字就能够轻松解决,但是看到测试用例,又发现没那么简单

import type { Equal, Expect } from '@type-challenges/utils'

type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>

type cases = [
  Expect<Equal<MyAwaited<X>, string>>,
  Expect<Equal<MyAwaited<Y>, { field: number }>>,
  Expect<Equal<MyAwaited<Z>, string | number>>,
]

// @ts-expect-error
type error = MyAwaited<number>

结合题目给的README和测试case,我们就能够得出,题目是需要我们实现一个函数,能够获取到 Promise 中的返回值的类型,但是这里要注意的是,测试中的 Promise 是能够嵌套多个的,并且当传入 number 的时候要给出报错。

利用js进行对比学习

通过 JS 我们能够有很多种方式来获取到一个Promise对象里面的值,then方法 或者 async/await 都是简单的获取返回值的方法,所以在这里就不做太多的介绍,直接快进到 TS 获取返回值类型。

实现 Awaited

先根据之前介绍的 infer 关键字获取到 Promise 的一个返回值:

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

这时候会发现测试例子还剩下两处报错,然后我们再来一个个解决。

typescript 类型体操 之 189-easy-awaited

递归取参

首先看一个报错的测试例子:

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

type Z = Promise<Promise<string | number>>;

type A = MyAwaited<Z>
// type A = Promise<string | number>

错误的原因很明显,我们只获取了第一层的 Promise 返回值,但是 Promise 可能会存在多层嵌套

那么这里就要涉及到 TS 泛型中的递归的概念,这和 JS 中的递归是完全一样的,我们可以利用条件链来实现一个泛型的递归。

我们再来看一下之前写的泛型:

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

换一种写法可能就会比较容易发现,我们能够在true分支当中,继续对拿到的 R 进行判断,从而实现递归。当拿到的 R 还是一个被 Promise 包裹的类型的话,我们就将它在此丢到 MyAwaited 中,不然的话就返回。

type MyAwaited<T> = T extends Promise<infer R> 
    ? R extends Promise<any> 
        ? MyAwaited<R>
        : R
    : never;

这样子我们就能够成功拿到嵌套的 Promise 中的类型。 typescript 类型体操 之 189-easy-awaited

入参约束

这时候测试中还存在另一个错误,就是参数约束的问题

typescript 类型体操 之 189-easy-awaited

简单的给入参加上约束,这道题的测试就成功全部通过了。

type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer R> 
    ? R extends Promise<unknown> 
        ? MyAwaited<R>
        : R
    : never;

知识点

关于上述提到了部分的知识点:

  1. infer 关键字
  2. 条件链

infer 关键字我们在之前的文章当中有介绍过,条件链可以参考官方文档中的介绍

不了解条件类型的话也可以优先看一下之前介绍条件类型的文章:

总结

今天这道题的知识点可能是之前没有见到过的,关系到了条件链和 infer 关键字,这两个知识点是之后做类型体操的一个很重要的基础,很有必要去掌握明白再来进行刷题。

转载自:https://juejin.cn/post/7103545848570576904
评论
请登录