暮年|细数这些年被困扰过的 TS 问题( 六 )

  • ObjectConstructor 接口定义了 Object 类的属性 。
// node_modules/typescript/lib/lib.es5.d.tsinterface ObjectConstructor {/** Invocation via `new` */new(value?: any): Object;/** Invocation via function calls */(value?: any): any;readonly prototype: Object;getPrototypeOf(o: any): any;// ···}declare var Object: ObjectConstructor;Object 类的所有实例都继承了 Object 接口中的所有属性 。
7.3 {} 类型{} 类型描述了一个没有成员的对象 。 当你试图访问这样一个对象的任意属性时 , TypeScript 会产生一个编译时错误 。
// Type {}const obj = {};// Error: Property 'prop' does not exist on type '{}'.obj.prop = "semlinker";但是 , 你仍然可以使用在 Object 类型上定义的所有属性和方法 , 这些属性和方法可通过 JavaScript 的原型链隐式地使用:
// Type {}const obj = {};// "[object Object]"obj.toString(); 继续阅读:一文读懂 TS 中 Object, object, {} 类型之间的区别八、数字枚举与字符串枚举之间有什么区别8.1 数字枚举在 JavaScript 中布尔类型的变量含有有限范围的值 , 即 true 和 false 。 而在 TypeScript 中利用枚举 , 你也可以自定义相似的类型:
enum NoYes {No,Yes,}No 和 Yes 被称为枚举 NoYes 的成员 。 每个枚举成员都有一个 name 和一个 value 。 数字枚举成员值的默认类型是 number 类型 。 也就是说 , 每个成员的值都是一个数字:
enum NoYes {No,Yes,}assert.equal(NoYes.No, 0);assert.equal(NoYes.Yes, 1);除了让 TypeScript 为我们指定枚举成员的值之外 , 我们还可以手动赋值:
enum NoYes {No = 0,Yes = 1,}这种通过等号的显式赋值称为 initializer 。 如果枚举中某个成员的值使用显式方式赋值 , 但后续成员未显示赋值 ,TypeScript 会基于当前成员的值加 1 作为后续成员的值 。
8.2 字符串枚举除了数字枚举 , 我们还可以使用字符串作为枚举成员值:
enum NoYes {No = 'No',Yes = 'Yes',}assert.equal(NoYes.No, 'No');assert.equal(NoYes.Yes, 'Yes');8.3 数字枚举 vs 字符串枚举数字枚举与字符串枚举有什么区别呢?这里我们来分别看一下数字枚举和字符串枚举编译的结果:
数字枚举编译结果
"use strict";var NoYes;(function (NoYes) {NoYes[NoYes["No"] = 0] = "No";NoYes[NoYes["Yes"] = 1] = "Yes";})(NoYes || (NoYes = {}));字符串枚举编译结果
"use strict";var NoYes;(function (NoYes) {NoYes["No"] = "No";NoYes["Yes"] = "Yes";})(NoYes || (NoYes = {}));通过观察以上结果 , 我们知道数值枚举除了支持 从成员名称到成员值 的普通映射之外 , 它还支持 从成员值到成员名称 的反向映射 。 另外 , 对于纯字符串枚举 , 我们不能省略任何初始化程序 。 而数字枚举如果没有显式设置值时 , 则会使用默认值进行初始化 。
8.4 为数字枚举分配越界值讲到数字枚举 , 这里我们再来看个问题:
const enum Fonum {a = 1,b = 2}let value: Fonum = 12; // Ok相信很多读者看到 let value: Fonum = 12; 这一行 , TS 编译器并未提示任何错误会感到惊讶 。 很明显数字 12 并不是 Fonum 枚举的成员 。为什么会这样呢?我们来看一下 TypeScript issues 26362 中 DanielRosenwasser 大佬的回答:
The behavior is motivated by bitwise operations. There are times when SomeFlag.Foo | SomeFlag.Bar is intended to produce another SomeFlag. Instead you end up with number, and you don't want to have to cast back to SomeFlag.