let 与 const

let 与 const 唯一的区别是,两者声明的变量,对于初始化之后的行为,let 变量是可写的,const 变量(或者叫做常量)是不可写的。

注意,在 JavaScript 中的常量和一般语言中的常量含义不同。JavaScript 中的常量的含义是:“只能在初始化时候赋值一次,从此都不可以再次赋值的变量”,举个最简单的例子:

  1. const a = new Date

这是 JS 中的 const,但是不是编译期可以确定的量,但是声明后,不允许对其修改,只能读取。

而某些其他语言的常量的含义是:“在编译期就可以确定的量,可以进行常量折叠”。为了常量折叠,他们可能做很多奇奇怪怪的限制,比如 go 语言,就限定量 const 不能调用函数。

字面量类型

字面量(Literal Type)主要分为 真值字面量类型(boolean literal types),数字字面量类型(numeric literal types),枚举字面量类型(enum literal types) ,大整数字面量类型(bigInt literal types)和字符串字面量类型(string literal types)。

下面就是一些示例:

  1. const a: 2333 = 2333
  2. const ab : 0b10 = 2
  3. const ao : 0o114 = 0b1001100
  4. const ax : 0x514 = 0x514
  5. const b : 0x1919n = 6425n
  6. const c : 'xue xi qiang guo' = 'xue xi qiang guo'
  7. const d : false = false
  8. const foo: 'foo' = 'foobar' // Type '"foobar"' is not assignable to type '"foo"'.

字面量类型的要和实际的值的字面量一一对应。如果值不是同一个实例,则会报错。
每个字面量类型都只有一个实例,所以它们也是 unit type。

(注意,有多种写法不代表不同,比如 76 和 0o114 和 0b1001100 在值声明都代表的是同一个值,在类型声明空间都代表同一个类型)

子类型关系

子类型(subtyping)是类型上的二元关系。

在编程语言理论中,子类型(动名词,英语:subtyping)是一种类型多态的形式。这种形式下,子类型(名词,英语:subtype)可以替换另一种相关的数据类型(超类型,英语:supertype)。也就是说,针对超类型元素进行操作的子程序、函数等程序元素,也可以操作相应的子类型。如果 S 是 T 的子类型,这种子类型关系通常写作 S <: T,意思是在任何需要使用 T 类型对象的环境中,都可以安全地使用 S 类型的对象。子类型的准确语义取决于具体的编程语言中“X 环境中,可以安全地使用 Y”的意义。编程语言的类型系统定义了各自不同的子类型关系。 由于子类型关系的存在,某个对象可能同时属于多种类型,因此,子类型(英语:subtyping)是一种类型多态的形式,也被称作子类型多态(英语:subtype polymorphism)或者包含多态(英语:inclusion polymorphism)。在面向对象程序设计中,多态一般仅指这里所说的“子类型多态”,而“参数多态”则一般被称作泛型程序设计。 子类型与面向对象语言中(类或对象)的继承是两个概念。子类型反映了类型(即面向对象中的接口)之间的关系;而继承反映了一类对象可以从另一类对象创造出来,是语言特性的实现。因此,子类型也称接口继承;继承称作实现继承。

用 TypeScript 中的符号,如果 S 是 T 的子类型,则记为 S extends T.

  1. const a : 114514 = 114514
  2. const b : number = 114514
  3. const c : unknown = 114514
  4. const d : any = 114514

上面的每一个赋值都是合法的。数字“114514”可以有多种不同的用途(作为字面量类型的实例,作为 number 的实例,作为 unknown 的实例,作为 any 的实例),就是因为子类型多态。

114514 : 114514 <: number <: unknown

上面的冒号左边是值,右边是类型

其中子类型关系还分为结构子类型(structural subtyping) 和名义子类型(nominal subtyping),这个我们后续章节会谈。

const 和 let 的 类型推断

  1. const a = 1 // type of a is 1
  2. let b = 1 // type of b is number
  3. let c = 1 as const // type of c is 1

let 是可读可写的,所以会被尽量推断成实例数大于1的最小类型,所以就被推断成了 number,而 const 是只读的,就被推断成了字面量类型。

我们可以通过 Const contexts for literal expressions 的功能把它指定到一个只读的类型。