【问题标题】:Advanced Conditional Types - Infer syntax高级条件类型 - 推断语法
【发布时间】:2020-11-16 07:56:58
【问题描述】:

简介

我一直在玩 TS 的一些高级功能,发现了一些你可以做的很酷的事情。引起我注意的一件很酷的事情是提取数组中每个元素的所有类型。但是,我发现这个特定示例中的语法令人困惑。

我在此评论下方直接突出显示的两个示例中最令人困惑的部分出现在两个示例中,但略有不同。

问题线

T extends (infer U)[]

示例 1

type ArrayTypes<T> = 
    T extends (infer U)[]
    ? U
    : never

let arr = [1, "2", []];
type test = ArrayTypes<typeof arr> // type test = string | number | any[]

示例 2

type ArrayTypes<T> = 
    T extends (infer U)
    ? U
    : never

let arr = [1, "2", []];
type test = ArrayTypes<typeof arr> // type test = (string | number | any[])[]

问题

现在,我了解泛型(大部分),所以我了解 T 代表什么,我也了解 extends 做什么(大部分)。我也明白 U 可以用来推断一个类型,当一个泛型类型可以被使用时。

我没有具体了解的是括号和方括号在两个示例中的作用和方式。我将不胜感激全面崩溃。如果您需要更多地解释如何推断或扩展工作,请随意,但就像我说的,我想我明白这些概念......也许没有。真正令人困惑的一件事是,如果我把 () / 括号拿走,那么一切都会搞砸。这是为什么呢?

我对以下假设的思考是否正确?

  1. 在幕后,在类型空间中,括号是定义联合类型的更严格的方式?因此打字稿将变成以下内容
type test2 = string | number

进入


type test2 = ( string | number )

注意括号。

  1. (推断 U)是说 - 定义一个新的联合类型,但推断它并将其分配给 U。它也构成了数组的类型?
T extends (infer U)[] 

谢谢:)

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:
    类型中的

    括号仅用于分组和解决歧义;它们不会修改它们所包围的类型。例如,给定类型

    type A = { a: 1, c: 3 };
    type B = { b: 2, c: 3 };
    

    那么这个类型解析成什么?

    type Z = keyof A | B; // ??
    

    使用括号,您可以明确说明您的解释方式:

    type X = (keyof A) | B; // "a" | "c" | B
    type Y = keyof (A | B); // "c"
    

    事实证明,keyofA 的绑定比联合运算符 | 更紧密,所以 Z 等价于 X... 所以如果你的意思是 Y,你需要这些括号.

    (越来越过时的)TypeScript Specification document 说:

    当它们用作数组元素类型时,联合、交集、函数或构造函数类型需要括号;围绕交集类型中的联合、函数或构造函数类型;以及联合类型中的函数或构造函数类型。

    如有疑问,请添加括号。


    类型中的

    方括号实际上有多种不同的含义:

    • T 类型后面紧跟一对empty 方括号时,表示一个数组,其元素为T 类型,等价于到Array&lt;T&gt;。所以{a: string}[] 是一个数组,其元素的类型为{a: string},并且等价于Array&lt;{a: string}&gt;。 (类似地,如果这种类型的前面紧跟readonly 修饰符,如readonly T[],那么它等价于ReadonlyArray&lt;T&gt;。)

    • 当一个类型 T 紧跟在方括号中的另一个类型 U (即T[U])...括号不是空的,这意味着你looking up or indexing T 的属性类型,其键为 U 类型。所以{a: string}["a"]{a: string}a属性的类型...即string。而Array&lt;boolean&gt;[number] 是当您使用number 键索引到Array&lt;boolean&gt; 时获得的类型......即boolean

    • 当方括号包含零个或多个类型的逗号分隔列表时,例如 [][T][T,U][T,U,V] 等,(如果它是零或一,你需要小心不要将其与上面的数组或查找符号混淆),然后您指定tuple 类型,该数组具有固定数量的可能不同类型的元素以固定顺序排列。元组类型也可以在readonly 前面加上readonly tuples,元组还有其他有趣的事情,比如rest and optional elements in tuple typesvariadic tuple types,所以readonly [0, ...[1, 2, 3], 4?, ...[5?, 6?, 7?], ...8[]] 是一个有效的TS 类型(在TS4.0 +)。


    有了这些,让我们看看这些例子:

    type ArrayTypes<T> = 
        T extends (infer U)[]
        ? U
        : never
    

    这就是说:给定一个类型T,如果它扩展了某个数组类型U[],其中U将被推断出来,则返回U,否则返回neverinfer 需要附加到 U。无论出于何种原因,[]infer 绑定得更紧密,因此T extends infer U[] 将被解释为T extends infer (U[])...这是无效的,因为您只能infer 一个新类型变量,而不是这样的函数一个变量。这就是你需要括号的原因。

    let arr = [1, "2", []];
    type test = ArrayTypes<typeof arr> // type test = string | number | never[]
    

    这很有道理,对吧? arr 被推断为Array&lt;string | number | Array&lt;never&gt;&gt; 或等效的(string | number | never[])[] 类型,因此ArrayTypes&lt;typeof arr&gt; 推断Ustring | number | never[],这就是你得到的。 (请注意,在我使用的 TS 版本中,[] 的值被推断为类型 never[],而不是 any[])。

    另外请注意,上面带有number 键的查找类型在不需要infer 的情况下执行相同的操作:

    type ArrayTypes<T> = T extends any[] ? T[number]: never;
    

    下一个例子:

    type ArrayTypes<T> = 
        T extends (infer U)
        ? U
        : never
    

    这里的括号不是必需的,因为没有歧义。你可以写T extends infer U ? U : never。这意味着:给定一个类型T,它扩展了一些推断类型U,返回U;否则返回never。但这似乎有点傻,因为U 将永远被推断为T,因此ArrayTypes&lt;T&gt; 将永远是T,无论如何:

    let arr = [1, "2", []];
    type test = ArrayTypes<typeof arr> // type test = (string | number | never[])[]
    

    test 类型将与typeof arr 相同,即Array&lt;string | number | Array&lt;never&gt;&gt;,或等效的(string | number | never[])[]


    我希望这可以为您澄清事情。祝你好运!

    Playground link to code

    【讨论】:

    • 非常感谢您的回复,就像有人一口气解释了整个宇宙(启发性)。 :)
    • 非常感谢您的解释。这对我帮助很大:“事实证明,keyof 与 A 的绑定比联合运算符 | 更紧密”。该声明使一切变得更加清晰。
    猜你喜欢
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2018-10-20
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多