Background:
The Symbol type was introduced in JavaScript (ES6) to create unique identifiers that are guaranteed not to collide with other object properties. TypeScript has supported symbols since the inclusion of ES6 compatibility.
Problem:
Before Symbol, strings were commonly used as keys for object properties. This led to errors when extending or reusing objects: random name collisions and the inability to hide private properties (even via convention). Symbol allowed for the creation of unique, externally invisible keys, but questions about typing arose — how to describe types with Symbol keys and use them safely in APIs?
Solution:
TypeScript supports symbols as values and types; however, the typing of Symbol keys has its peculiarities. To create a symbol, you can use the global constructor or the global symbol registry. In interfaces or types, keys with symbols must explicitly indicate the type as symbol, and access to such properties is only done with a saved reference to the Symbol.
Code example:
const SECRET = Symbol('secret'); interface SecretObject { [SECRET]: string; visible: string; } const obj: SecretObject = { visible: 'see me', [SECRET]: 'hidden', }; console.log(obj.visible); // 'see me' // console.log(obj["secret"]); // Error: property does not exist! console.log(obj[SECRET]); // 'hidden'
Key features:
Can a Symbol be automatically converted to a string when used in objects?
No, a Symbol cannot be automatically converted to a string; an attempt to do so (e.g., via concatenation) will result in an error.
const mySymbol = Symbol('desc'); // alert('prefix_' + mySymbol); // TypeError
Can Symbol keys be enumerated via Object.keys?
No, Object.keys and for...in ignore Symbol keys. To retrieve such keys, Object.getOwnPropertySymbols is used.
const sym = Symbol('a'); const obj = { [sym]: 42 }; Object.keys(obj); // [] Object.getOwnPropertySymbols(obj); // [Symbol(a)]
Are properties with Symbol keys passed when copying via Object.assign?
Yes, Object.assign copies both string and symbol keys, unlike JSON.stringify.
const s = Symbol('s'); const o1 = { [s]: 123, foo: 'bar' }; const o2 = Object.assign({}, o1); o2[s]; // 123
A developer used string keys ('_private') for private properties, relying on convention. In Team B, a similar string was accidentally added — the properties collided, resulting in an unpredictable error.
Pros:
Cons:
A second developer used Symbol for hidden properties (e.g., Symbol('internal')). Now even within the team, internal data cannot be accidentally overridden: access is only possible if there is a reference to the specific Symbol.
Pros:
Cons: