【问题标题】:How to undestand relations between types any, unknown, {} and between them and other types?如何理解类型 any、unknown、{} 之间以及它们与其他类型之间的关系?
【发布时间】:2019-12-02 08:42:00
【问题描述】:

试图了解类型之间的关系我有这段代码

type CheckIfExtends<A, B> = A extends B ? true : false;

type T1 = CheckIfExtends<number, unknown>; //true
type T2 = CheckIfExtends<number, {}>; //true
type T3 = CheckIfExtends<number, any>; //true
type T4 = CheckIfExtends<() => void, unknown>; //true
type T5 = CheckIfExtends<() => void, {}>; //true
type T6 = CheckIfExtends<() => void, any>; //true
type T7 = CheckIfExtends<unknown, any>; //true
type T8 = CheckIfExtends<any, unknown>; //true
type T9 = CheckIfExtends<{}, unknown>; //true
type T10 = CheckIfExtends<{}, any>; //true
type T11 = CheckIfExtends<any, {}>; //boolean
type T12 = CheckIfExtends<unknown, {}>; //false

Link to playground

有人能解释一下吗?有什么区别? any extends {}any 怎么可能不会同时扩展 {}?如果any extends unknownunknown extends any 那么这是否意味着它们是强相等的?是 JavaScript 的 nullundefinded 股权问题之上 Typescript 的新缺陷吗?

其实

type T = CheckIfExtends<any, number>; //boolean

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    区别本质上是这样的:

    • the any type 是故意不合理的,因为它可以分配给和任何其他类型(never 可能除外,具体取决于您使用它的位置)。不健全意味着某些类型的基本规则被破坏,例如transitivitysubtyping。通常,如果A 可分配给BB 可分配给C,则A 可分配给C。但是any 打破了这一点。例如:string 可分配给anyany 可分配给number...但string不能分配给number。这种特殊的不健全性非常有用,因为它允许我们在部分代码中“关闭”类型检查,而这部分代码要么难以正确输入,要么不可能正确输入。但是您需要非常小心地将any 视为一种类型;它更像是一种“非类型”。

    • the empty type, {}, 是一种可以在运行时被视为对象的类型(也就是说,您可以从中读取属性或方法而不会出现运行时错误),但它没有 已知 属性编译时间。这并不意味着它没有属性。它只是意味着编译器不知道它们中的任何一个。这意味着只有nullundefined 不能分配给{}null.fooundefined.foo 是运行时错误)。即使像string 这样的原始类型也可以被视为在运行时具有属性和方法("".length"".toUpperCase() 有效,甚至"".foo 只返回undefined)。当然,任何实际的对象类型也将分配给{}

      另一方面,{} 类型不能分配给很多类型。如果我有{} 类型的值,尝试将其分配给{foo: string} 类型的变量,则会出现编译器错误,因为不知道{} 包含foo 属性。您可以将{} 分配给它自己,或者分配给更广泛的类型,例如unknown,或者分配给“un-type”any

      这使得{} 非常几乎 a top type,这是一个所有其他类型都可以分配的类型。它本质上是一个移除了nullundefined 的顶级类型。

    • the unknown type 是在 TypeScript 3.0 中引入的,是 true 顶级类型; TypeScript 中的每个类型都可以分配给unknown。甚至nullundefined 也可以分配给unknown

      另一方面,unknown 只能分配给它自己和“un-type”any。即使{} 类型也不够宽,您无法将unknown 分配给它。从概念上讲,您应该能够将unknown 分配给联合类型{} | null | undefined,但这是intentionally not implemented,以保持unknown 作为“真正的”顶级类型。


    您的大部分CheckIfExtends&lt;A, B&gt; 结果都可以通过上述来解释。例外是T11

    type T11 = CheckIfExtends<any, {}>; //boolean
    

    您的CheckIfExtends&lt;A, B&gt; 类型定义是distributive conditional type,当A 是联合类型时,它会做一些有趣的事情,因为如果联合的各个部分都满足两个分支,则它允许采用条件的两个分支.当Aany 时,它也会执行相同的分布,除非Banyunknown(所以T8 表现正常)。 microsoft/TypeScript#27418 对此进行了一些讨论。不管怎样,T11 接受两个分支,你会得到true | false,即boolean。 (来自 microsoft/TypeScript#27418,A 位置中的unknown 不会分发,因此T7T12 的行为也正常。


    好的,希望对您有所帮助;祝你好运!

    【讨论】:

      【解决方案2】:

      我将从 TypeScript 中的 extends 的含义开始。乍一看,它的行为很奇怪,因为产品类型(如对象)的行为类似于 'is superset',而 unions 的行为类似于 'is subset',它也完全不同地适用于函数类型。乍一看可能很奇怪,但这是合乎逻辑的行为,换句话说,大多数类型都有soundness property

      我理解这个概念的经验法则是将extends读作assignable to。那么如果x扩展了y,则意味着x可以在需要y时使用。

      让我们考虑三种不同的代数数据类型,如果上面对它们成立的话。

      产品类型

      type A = {a: string}
      type B = {a: string; b: number}
      type BextendsA = B extends A ? true : false // evaluates to true 
      

      以上是正确的,因为 B 可以在需要 A 的每个地方使用,因为 B 涵盖了 A 的整个结构。 B 是 A 的超集。但这里成立的是 B 可以分配给 A。

      联合类型

      type A = number | string
      type B = number
      type BextendsA = B extends A ? true : false // evaluates to true 
      

      它寻找联合的方式完全不同。 B 代表产品是一个超集,因为联合 B 是一个子集!是的,不同的逻辑,B 并不代表 A 中所有可能的值,但只要需要 A,就可以使用 B 代替。所以 B 可以分配给 A。

      对于函数类型

      type A = (a: number) => void
      type B = () => void
      type BextendsA = B extends A ? true : false // evaluates to true 
      

      对于函数类型,它看起来更奇怪,因为 A 看起来比 B 更指定函数,那么 B 如何扩展 A 呢?这又来自可分配性,因为只要有东西需要 A,我们就可以分配 B。这在 Array.map 这样的示例中非常明显。考虑:

      [1,2].map(x => x + 1)
      

      Array.map 需要具有三个参数的函数 - (el,index,arr) =&gt; any,但可以使用只有一个参数 el =&gt; any 的函数。并且它再次拥有可分配性,B 可分配给 A。


      anyunknown{} 这样的类型是不健全的,这意味着它们的行为无法在逻辑上得到证明。了解它们在 TS 中的行为方式更多的是了解有关此类决策的规范和原因。但这无法从逻辑上解释,因为不健全的类型会违反逻辑。

      仔细考虑了 TypeScript 允许不良行为的地方,并且在整个文档中,我们将解释这些情况发生的位置以及它们背后的动机。

      【讨论】:

        猜你喜欢
        • 2020-03-26
        • 1970-01-01
        • 2019-03-25
        • 2012-08-07
        • 2018-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多