История вопроса:
Тип Symbol был добавлен в JavaScript (ES6) для создания уникальных идентификаторов, которые гарантированно не совпадут с другими свойствами объекта. TypeScript поддерживает символы с момента включения ES6-совместимости.
Проблема:
До появления Symbol в роли ключей для свойств объектов часто использовались строки. Это приводило к ошибкам при расширении или переиспользовании объектов: случайные коллизии имен и невозможность скрыть приватные свойства (даже через convention). Symbol позволил создавать уникальные, невидимые внешнему коду ключи, но появились вопросы типизации — как описывать типы с Symbol-ключами и безопасно использовать их в API?
Решение:
TypeScript поддерживает символы как значения и типы, однако сама типизация Symbol-ключей обладает особенностями. Для создания символа можно использовать глобальный конструктор или глобальный реестр символов. В интерфейсах или типах ключи с символами должны явно указывать тип как symbol, а доступ к таким свойствам осуществляется только с сохранённой ссылкой на Symbol.
Пример кода:
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"]); // Ошибка: такого свойства нет! console.log(obj[SECRET]); // 'hidden'
Ключевые особенности:
Может ли Symbol быть приведён к строке автоматически при использовании в объектах?
Нет, Symbol нельзя автоматически конвертировать в строку, попытка сделать это (например, через конкатенацию) приведёт к ошибке.
const mySymbol = Symbol('desc'); // alert('prefix_' + mySymbol); // TypeError
Можно ли перечислить Symbol-ключи через Object.keys?
Нет, Object.keys и for...in игнорируют Symbol-ключи. Для получения таких ключей используется Object.getOwnPropertySymbols.
const sym = Symbol('a'); const obj = { [sym]: 42 }; Object.keys(obj); // [] Object.getOwnPropertySymbols(obj); // [Symbol(a)]
Передаются ли свойства с Symbol-ключами при копировании через Object.assign?
Да, Object.assign копирует как строковые, так и символические ключи, в отличие от JSON.stringify.
const s = Symbol('s'); const o1 = { [s]: 123, foo: 'bar' }; const o2 = Object.assign({}, o1); o2[s]; // 123
Разработчик использовал для приватных свойств строковые ключи ('_private'), полагаясь на convention. В Team B случайно добавили такую же строку — свойства перекрылись, возникла непредсказуемая ошибка.
Плюсы:
Минусы:
Второй разработчик применил Symbol для скрытых свойств (например, Symbol('internal')). Теперь даже внутри команды нельзя случайно перекрыть внутренние данные: доступ возможен только при наличии ссылки на конкретный Symbol.
Плюсы:
Минусы: