使用 typeof 时扩大行为

Widening behaviour when using typeof

我试图了解 TypeScript 在扩展某些文字类型时的行为。我创建了一个代码示例来解释:Playground.

我有 4 个我分配给 let 变量的文字类型示例:

let A = 1;
let B = 'blah';
let C = true;
let D = Shapes.Rectangle;

如果您将鼠标悬停在 playground 中的每个 let 上,您会看到字面量类型已扩展为所有四种类型所预期的更宽泛的类型。但是,当在测试中与 typeof 结合使用时,只有 AB 扩大到更广泛的类型,而 CD 则不会。

谁能解释这种行为?

Typescript编译器需要静态决定变量的类型 在整个程序中不应更改。

因此,当它不确定确切的类型时,它默认为它可以解析的最接近的更宽类型。

因为 let 声明可以在我们执行的某个地方改变, 它做出实用的猜测并分配其类型 number booleanstring 和其他 enum 类型

也是如此

使用 let 有可能我们可以在其他地方将 D 重新分配为 Shapes.Square 所以在那种情况下,我们不希望我们的类型不同步并且行为随机!

但是如果我们把它设为const,我们可以保证它们在这个程序的整个执行过程中不会改变 因此,打字稿编译器可以安全地推断出文字分配的类型

如果您希望类型是实际类型而不是扩展的类型,您可以随时对其进行注释! let D: Shapes.Rectangle = Shapes.Rectangle

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

enum Shapes {
  Square = 'square',
  Rectangle = 'rectangle'
}

const A = 1; 
const B = 'blah';
const C = true;
const D = Shapes.Rectangle;

type tests = [
  Expect<Equal<typeof A, 1>>,
  Expect<Equal<typeof B, 'blah'>>,
  Expect<Equal<typeof C, true>>,
  Expect<Equal<typeof D, Shapes.Rectangle>>
]

Code Playground

现在回答你的问题,为什么 Typescript 会缩小某些类型(booleanEnum),而扩大其他类型(numberstring

原因很简单,Typescript 编译器足够聪明,可以检查类型集是否包含 finite 类型元素,然后它可以根据用法推断出它。

Ex 1: type boolean = true | false // 有限集

例 2:enum Shapes { Square = 'square', Rectangle = 'rectangle'} // 有限集

这里我们定义了FiniteTypes别名构成number | string | any[] 因此,当我们将值重新分配给这些类型中的任何一种时,typeof 可以确定当前变量中的类型,因为它可以遍历成分

而在 numberstring 等类型的情况下,这些类型有无限的成分,因此我们不会根据其重新分配来缩小类型范围。

例子

// Proof that TS compiler is smart enough to loop through Finite elements of type

type FiniteTypes = number | string | any[]

let z: FiniteTypes = 2
type t_1 = typeof z // Inferred as number

z = [1,2,3]
type t_2 = typeof z // Inferred as any[]

z = "Foo bar"
type t_3 = typeof z // Inferred as string


Example for narrowing types based on Assignment