挑战

如果我们有一个包装类型,比如Promise 我们如何获得包装类型的内部类型? 例如,如果 我们有Promise<ExampleType>如何得到ExampleType ?

解答

这是一个非常有趣的挑战,它要求我们了解 TypeScript 的一个被低估的特性,恕我直言。

但是,在说明我的意思之前,让我们来分析一下这个挑战。作者要求我们展开类型。什么是 展开? 展开是从另一个类型中提取内部类型。

让我用一个例子来说明。如果你有一个类型Promise<string>,展开Promise类型将得到 类型string。我们从外部类型得到其内部类型。

注意,你还需要递归地展开类型。例如,如果你有类型Promise<Promise<string>>,你需 要返回类型string

现在,言归正传。我将从最简单的例子开始。如果我们的Awaited类型得 到Promise<string>,我们需要返回string,否则我们返回T本身,因为它不是一个 Promise:

type Awaited<T> = T extends Promise<string> ? string : T;

但是有一个问题。这样,我们只能处理string类型在Promise中的情况,而我们需要的 是可以处理任何情况。怎么做呢? 在我们不知道类型的情况下,如何从Promise获取类型?

出于这些目的,TypeScript 在条件类型中有类型推断功能! 你可以对编译器说”嘿,一旦你 知道了类型是什么,请把它赋给我的类型参数”。你可以在这里阅读更多关 于条件类型中的类型推断

了解了类型推断之后,我们可以更新我们的解答。我们没有在条件类型中检 查Promise<string>,而是将string替换为infer R,因为我们不知道那里必须有什 么。我们只知道它是Promise<T>,其内部包含某种类型。

一旦 TypeScript 确定了Promise中的类型,它就会把它赋给我们的类型参数R,并在 “true”分支中可用。我们正是从这里返回它的:

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

我们几乎完成了,但从类型Promise<Promise<string>>我们得到类 型Promise<string>。因此,我们需要递归地重复相同的过程,这是通过调用Awaited本 身来实现的:

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

参考