高级 TypeScript 练习 - 答案 1

2025-06-07

高级 TypeScript 练习 - 答案 1

我问的问题是:

如果我们有一个像 这样的包装类型Promise,那么我们如何获取包装类型内部的类型呢?例如,如果我们有 ,Promise<ExampleType>该如何获取ExampleType

答案

type Transform<A> = A extends Promise<infer Inner> ? Inner : never
type Result = Transform<Promise<string>> // Result is string type
Enter fullscreen mode Exit fullscreen mode

为了解开承诺类型,我们使用了infer关键字

关键字对任何类型构造函数都有帮助,类型构造函数是由另一个类型变量参数化的类型,因此任何具有通用占位符的类型,如A<B>,其中A类型构造函数由参数化B

使用示例infer

我们infer也可以与其他类型一起使用,例如数组

type InsideArray<A> = A extends Array<infer Inside> ? Inside : never
type Str = InsideArray<Array<string>>; // Str is string
Enter fullscreen mode Exit fullscreen mode

那么自定义参数化类型呢?可以!

type Surprise<A> = { inside: A }
type UnpackSurprise<S> = S extends Surprise<infer Inside> ? Inside : never
type Num = UnpackSurprise<Surprise<number>> // Num is number
Enter fullscreen mode Exit fullscreen mode

我们甚至可以使用它infer来获取映射类型属性

type User = {
    id: number,
    name: string,
}

type Doc = {
    id: string,
}

type GetProperty<T, Prop extends keyof T> = T extends { [K in Prop]: infer Value } ? Value : never

type UserId = GetProperty<User, 'id'>
type DocId = GetProperty<Doc, 'id'>
Enter fullscreen mode Exit fullscreen mode

问题:如何更简单地获取 Mapped 类型属性的类型?请在评论中写下你的答案!

我们可以使用多个类型变量并推断它们吗?当然可以!

type ABC<A, B, C> = { a: A, b: B, c: C }
type ABCIntoTuple<T> 
  = T extends ABC<infer A, infer B, infer C> ? [A, B, C] : never
type Example = ABC<string, boolean, number>
type ExampleTuple = ABCIntoTuple<Example> // [string, boolean, number]
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们推断出所有三个类型参数并将它们放入 3-n 元组中。

为什么never

类型never是一种底层类型,它是一种没有任何值的类型,它非常方便地构造,用于表示我们的函数不返回,或者代码的某些路径无法访问,有关它的更多信息,您可以阅读Marius Schulz 的精彩文章

我们使用never条件语句来表示不愉快的路径,我们说这是一条死胡同,如果你不传递给构造函数特定的类型,我们就没有其他选择,我们的类型无法与任何其他类型兼容。想象一下,当我们传递给它一些不符合条件的内容时,它会如何表现:

type Transform<A> = A extends Promise<infer Inner> ? Inner : never
type OhGosh = Transform<string> // OhGosh evaluates to never
Enter fullscreen mode Exit fullscreen mode

我们可以对负路径有不同的表示,但 never 是最佳选择,因为进一步的类型转换毫无意义。我们也可以在参数中设置约束,这样never路径就永远不会到达。

考虑以下改变:

type Transform<A extends Promise<any>> = A extends Promise<infer Inner> ? Inner : never
type OhGosh = Transform<string> // compilation error
Enter fullscreen mode Exit fullscreen mode

现在A extends Promise<any>我们的实用程序类型Transform是防弹的,因为对于不扩展的类型,编译将失败Promise<any>

我为什么要使用Promise<any>

我把 放进anyPromise是因为any是不健全的类型之一,也是可以分配给一切的类型,这意味着每个类型都从 扩展any,这决定了每种Promise类型都会扩展Promise<any>

本系列才刚刚开始。如果你想了解 TypeScript 高级教程中一些有趣的新问题,请在dev.toTwitter上关注我。

文章来源:https://dev.to/macsikora/advanced-typescript-exercises-answer-1-59ge
PREV
我希望在成为软件工程师之前被告知的事情
NEXT
掌握 useEffect API