TypeScript中的symbol类型对应于JavaScript中的Symbol原始类型。该类型能够表示任意的Symbol值。
symbol类型使用symbol关键字来表示。示例如下:
// 自定义Symbol
const key: symbol = Symbol();
// Well-Known Symbol
const symbolHasInstance: symbol = Symbol.hasInstance;
TypeScript字面量中介绍过,字面量能够表示一个固定值。例如,数字字面量“3”表示固定数值“3”;字符串字面量“’up’”表示固定字符串“’up’”。symbol类型不同于其他原始类型,它不存在字面量形式。symbol类型的值只能通过“Symbol()”
和“Symbol.for()”
函数来创建或直接引用某个“Well-Known Symbol”值。示例如下:
const s0: symbol = Symbol();
const s1: symbol = Symbol.for('foo');
const s2: symbol = Symbol.hasInstance;
const s3: symbol = s0;
为了能够将一个Symbol值视作表示固定值的字面量,TypeScript引入了“unique symbol”
类型。“unique symbol”类型使用“unique symbol”关键字来表示。示例如下:
const s0: unique symbol = Symbol();
const s1: unique symbol = Symbol.for('s1');
“unique symbol”类型的主要用途是用作接口、类等类型中的可计算属性名。因为如果使用可计算属性名在接口中添加了一个类型成员,那么必须保证该类型成员的名字是固定的,否则接口定义将失去意义。
下例中,允许将“unique symbol”类型的常量x
作为接口的类型成员,而symbol类型的常量y
不能作为接口的类型成员,因为symbol类型不止包含一个可能值:
const x: unique symbol = Symbol();
const y: symbol = Symbol();
interface Foo {
[x]: string; // 正确
[y]: string;
// ~~~
// 错误:接口中的计算属性名称必须引用类型为字面量类型
// 或'unique symbol'的表达式
}
运行结果:
实际上,“unique symbol”类型的设计初衷是作为一种变通方法,让一个Symbol值具有字面量的性质,即仅表示一个固定的值。“unique symbol”类型没有改变Symbol值没有字面量表示形式的事实。为了能够将某个Symbol值视作表示固定值的字面量,TypeScript对“unique symbol”类型和Symbol值的使用施加了限制。
TypeScript选择将一个Symbol值与声明它的标识符绑定在一起,并通过绑定了该Symbol值的标识符来表示“Symbol字面量”。这种设计的前提是要确保Symbol值与标识符之间的绑定关系是不可变的。因此,TypeScript中只允许使用const声明或readonly属性声明来定义“unique symbol”类型的值。示例如下:
// 必须使用const声明
const a: unique symbol = Symbol();
interface WithUniqueSymbol {
// 必须使用readonly修饰符
readonly b: unique symbol;
}
class C {
// 必须使用static和readonly修饰符
static readonly c: unique symbol = Symbol();
}
此例第1行,常量a
的初始值为Symbol值,其类型为“unique symbol”类型。在标识符a与其初始值Symbol值之间形成了绑定关系,并且该关系是不可变的。这是因为常量的值是固定的,不允许再被赋予其他值。标识符a
能够固定表示该Symbol值,标识符a的角色相当于该Symbol值的字面量形式。
如果使用let
或var
声明定义“unique symbol”类型的变量,那么将产生错误,因为标识符与Symbol值之间的绑定是可变的。示例如下:
let a: unique symbol = Symbol();
// ~
// 错误:'unique symbol' 类型的变量必须使用'const'
var b: unique symbol = Symbol();
// ~
// 错误:'unique symbol' 类型的变量必须使用'const'
“unique symbol”类型的值只允许使用“Symbol()”函数或“Symbol.for()”方法的返回值进行初始化,因为只有这样才能够“确保”引用了唯一的Symbol值。示例如下:
const a: unique symbol = Symbol();
const b: unique symbol = Symbol('coolcou');
const c: unique symbol = a;
// ~
// 错误:a的类型与c的类型不兼容
const d: unique symbol = b;
// ~
// 错误:b的类型与d的类型不兼容
但是,我们知道使用相同的参数调用“Symbol.for()”方法实际上返回的是相同的Symbol值。因此,可能出现多个“unique symbol”类型的值实际上是同一个Symbol值的情况。由于设计上的局限性,TypeScript目前无法识别出这种情况,因此不会产生编译错误,开发者必须要留意这种特殊情况。示例如下:
const a: unique symbol = Symbol.for('same');
const b: unique symbol = Symbol.for('same');
此例中,编译器会认为a和b是两个不同的Symbol值,而实际上两者是相同的。
在设计上,每一个“unique symbol”类型都是一种独立的类型。在不同的“unique symbol”类型之间不允许相互赋值;在比较两个“unique symbol”类型的值时,也将永远返回false。示例如下:
const a: unique symbol = Symbol();
const b: unique symbol = Symbol();
if (a === b) {
// ~~~~~~~
// 该条件永远为false
console.log('unreachable code');
}
输出结果:
由于“unique symbol”类型是 symbol类型的子类型,因此可以将“unique symbol”类型的值赋值给symbol类型。示例如下:
const a: unique symbol = Symbol();
const b: symbol = a;
如果程序中未使用类型注解来明确定义是symbol类型还是“unique symbol”类型,那么TypeScript会自动地推断类型。示例如下:
// a和b均为'symbol'类型,因为没有使用const声明
let a = Symbol();
let b = Symbol.for('');
// c和d均为'unique symbol'类型
const c = Symbol();
const d = Symbol.for('');
// e和f均为'symbol'类型,没有使用Symbol()或Symbol.for()初始化
const e = a;
const f = a;
酷客网相关文章:
评论前必须登录!
注册