阮一峰《TypeScript 教程》学习笔记——symbol 类型
1. 一段话总结
symbol 是 ES2015 引入的原始类型,TypeScript 中用 symbol 表示该类型,其核心特征是每个通过 Symbol() 生成的值均独一无二;unique symbol 是 symbol 的子类型,用于表示“单个具体的 Symbol 值”,仅能通过 const 声明(let 声明会报错),const 声明时可省略类型(默认推断为 unique symbol),可作为属性名(避免冲突)或类的 readonly static 属性;类型推断上,let 声明的 Symbol 变量推断为 symbol,const 声明的 Symbol 变量默认推断为 unique symbol,但赋值给其他 symbol 类型变量时推断结果会变为 symbol。
2. 思维导图

3. 详细总结
1. symbol 类型简介
- 背景与定位:Symbol 是 ES2015 新增的原始类型,用于表示独一无二的值,TypeScript 中通过
symbol关键字表示该类型。 - 生成与特性:
- 需通过
Symbol()函数生成值,无法通过字面量创建; - 核心特征:每个 Symbol 值均独一无二,即使通过相同方式生成,也不相等。
- 示例:
let x:symbol = Symbol(); let y:symbol = Symbol(); x === y; // false(值不相等)
- 需通过
2. unique symbol 类型(symbol 的子类型)
unique symbol 用于表示“单个具体的 Symbol 值”,解决了 symbol 类型无法定位到某一特定 Symbol 值的问题,其规则与用途如下:
(1)核心规则(含与 symbol 对比)
| 对比维度 | symbol 类型 | unique symbol 类型 |
|---|---|---|
| 声明方式 | let/const 均可 | 仅允许 const 声明(let 报错) |
| 类型默认推断 | let 声明时默认推断为 symbol | const 声明 Symbol() 时默认推断为 unique symbol |
| 值类型唯一性 | 包含所有 Symbol 值,不区分具体值 | 每个变量对应独立值类型(不同变量类型不同) |
| 赋值规则 | 可相互赋值(如 let a:symbol = b) | 不可直接赋值给其他 unique symbol 变量,需用 typeof(如 const b:typeof a = a) |
| 子类型关系 | 父类型 | 子类型(可赋值给 symbol,反之不行) |
- 错误案例(
unique symbol声明与赋值):let y:unique symbol = Symbol(); // 报错(let 不允许) const a:unique symbol = Symbol(); const b:unique symbol = a; // 报错(不同值类型,不可直接赋值) - 正确案例(
unique symbol赋值):const a:unique symbol = Symbol(); const b:typeof a = a; // 正确(通过 typeof 匹配同值类型)
(2)特殊情况:Symbol.for()
Symbol.for('key') 会返回相同的 Symbol 值(相同 key 对应同一值),但 TypeScript 无法识别此特性,导致:
- 两个通过
Symbol.for('key')声明的unique symbol变量,类型不同但值相等:const a:unique symbol = Symbol.for('foo'); const b:unique symbol = Symbol.for('foo'); a === b; // 实际值相等,但 TS 无法识别(语法无报错,逻辑上值相等)
(3)主要用途
- 作为对象属性名:
仅unique symbol可作为属性名(保证不与其他属性冲突),symbol类型因不固定具体值,用作属性名会报错:const x:unique symbol = Symbol(); const y:symbol = Symbol(); interface Foo {[x]: string; // 正确(unique symbol 可作属性名)[y]: string; // 报错(symbol 不可作属性名) } - 作为类的属性:
仅允许赋值给类的readonly static属性(两个限定符缺一不可,确保属性固定不变):class C {static readonly foo:unique symbol = Symbol(); // 正确static foo2:unique symbol = Symbol(); // 报错(缺少 readonly)readonly foo3:unique symbol = Symbol(); // 报错(缺少 static) }
3. 类型推断规则
TypeScript 会根据变量声明方式(let/const)和赋值对象,自动推断 Symbol 变量的类型,具体规则如下:
| 变量声明方式 | 赋值内容 | 推断类型 | 示例代码 |
|---|---|---|---|
let 声明 | 直接赋值 Symbol() | symbol | let x = Symbol(); // 类型:symbol |
const 声明 | 直接赋值 Symbol() | unique symbol | const x = Symbol(); // 类型:unique symbol |
const 声明 | 赋值给其他 symbol 变量 | symbol | let x = Symbol(); const y = x; // 类型:symbol |
let 声明 | 赋值给 unique symbol 变量 | symbol | const x = Symbol(); let y = x; // 类型:symbol |
4. 关键问题
问题1:为什么 unique symbol 类型的变量只能用 const 声明,不能用 let 声明?(侧重 unique symbol 的设计逻辑)
答案:unique symbol 的核心定位是“表示单个、固定不变的具体 Symbol 值”,而 let 关键字声明的变量允许后续修改值,与 unique symbol 需“值固定”的特性冲突。若允许 let 声明,后续可能将其他 Symbol 值赋值给该变量,导致其“具体单个值”的定位失效。因此 TypeScript 强制 unique symbol 变量需用 const 声明,通过 const 的“值不可修改”特性,确保 unique symbol 始终指向同一具体 Symbol 值。
问题2:symbol 类型和 unique symbol 类型是父子类型关系,为什么 unique symbol 可以赋值给 symbol,但 symbol 不能赋值给 unique symbol?(侧重子类型赋值规则)
答案:根据 TypeScript 子类型赋值原则——“子类型包含父类型的所有特性,且具备额外专属特性,因此子类型可替代父类型,父类型不可替代子类型”:
unique symbol是symbol的子类型:symbol类型包含所有 Symbol 值(不区分具体值),unique symbol在此基础上新增“指向具体单个值”的特性,因此unique symbol具备symbol的所有特性,可赋值给symbol类型变量;symbol不能赋值给unique symbol:symbol类型仅表示“任意 Symbol 值”,不具备unique symbol所需的“具体单个值”特性,若允许赋值,会导致unique symbol失去“定位具体值”的核心作用,因此 TypeScript 禁止该操作。
问题3:同样是 const 声明的 Symbol 变量,为什么有时推断为 unique symbol,有时推断为 symbol?(侧重类型推断的触发条件)
答案:const 声明的 Symbol 变量类型推断结果,取决于赋值内容是否为“直接生成的 Symbol 值”:
- 当
const变量直接赋值Symbol()时(如const x = Symbol()),TypeScript 识别到该变量指向“单个具体的新 Symbol 值”,因此推断为unique symbol; - 当
const变量赋值给其他symbol类型变量时(如let y = Symbol(); const x = y),TypeScript 无法确定y指向的是“具体单个值”还是“任意 Symbol 值”,为避免类型误判,会将x推断为更宽泛的symbol类型,而非unique symbol。
