使用 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
结合使用时,只有 A
和 B
扩大到更广泛的类型,而 C
和 D
则不会。
谁能解释这种行为?
Typescript编译器需要静态决定变量的类型
在整个程序中不应更改。
因此,当它不确定确切的类型时,它默认为它可以解析的最接近的更宽类型。
因为 let
声明可以在我们执行的某个地方改变,
它做出实用的猜测并分配其类型 number
boolean
、string
和其他 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>>
]
现在回答你的问题,为什么 Typescript 会缩小某些类型(boolean
、Enum
),而扩大其他类型(number
、string
)
原因很简单,Typescript 编译器足够聪明,可以检查类型集是否包含 finite
类型元素,然后它可以根据用法推断出它。
Ex 1: type boolean = true | false
// 有限集
例 2:enum Shapes { Square = 'square', Rectangle = 'rectangle'}
// 有限集
这里我们定义了FiniteTypes
别名构成number | string | any[]
因此,当我们将值重新分配给这些类型中的任何一种时,typeof
可以确定当前变量中的类型,因为它可以遍历成分
而在 number
、string
等类型的情况下,这些类型有无限的成分,因此我们不会根据其重新分配来缩小类型范围。
例子
// 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
我试图了解 TypeScript 在扩展某些文字类型时的行为。我创建了一个代码示例来解释:Playground.
我有 4 个我分配给 let 变量的文字类型示例:
let A = 1;
let B = 'blah';
let C = true;
let D = Shapes.Rectangle;
如果您将鼠标悬停在 playground 中的每个 let
上,您会看到字面量类型已扩展为所有四种类型所预期的更宽泛的类型。但是,当在测试中与 typeof
结合使用时,只有 A
和 B
扩大到更广泛的类型,而 C
和 D
则不会。
谁能解释这种行为?
Typescript编译器需要静态决定变量的类型 在整个程序中不应更改。
因此,当它不确定确切的类型时,它默认为它可以解析的最接近的更宽类型。
因为 let
声明可以在我们执行的某个地方改变,
它做出实用的猜测并分配其类型 number
boolean
、string
和其他 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>>
]
现在回答你的问题,为什么 Typescript 会缩小某些类型(boolean
、Enum
),而扩大其他类型(number
、string
)
原因很简单,Typescript 编译器足够聪明,可以检查类型集是否包含 finite
类型元素,然后它可以根据用法推断出它。
Ex 1: type boolean = true | false
// 有限集
例 2:enum Shapes { Square = 'square', Rectangle = 'rectangle'}
// 有限集
这里我们定义了FiniteTypes
别名构成number | string | any[]
因此,当我们将值重新分配给这些类型中的任何一种时,typeof
可以确定当前变量中的类型,因为它可以遍历成分
而在 number
、string
等类型的情况下,这些类型有无限的成分,因此我们不会根据其重新分配来缩小类型范围。
例子
// 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