Валидация значений типа Symbol в JavaScript требует отдельного
подхода, поскольку Symbol не является строкой, числом или объектом в
привычном смысле и обладает уникальной семантикой идентичности. В
библиотеке Joi для этого предусмотрен специализированный валидатор
Joi.symbol().
Конструктор Joi.symbol() создаёт схему, которая
допускает только значения типа Symbol.
import Joi from 'joi';
const schema = Joi.symbol();
schema.validate(Symbol('a')); // valid
schema.validate('Symbol(a)'); // error
schema.validate(123); // error
В основе проверки лежит строгая типизация: значение должно быть
результатом вызова Symbol() или
Symbol.for().
Symbol в JavaScript используется для создания уникальных
идентификаторов свойств объектов. Каждое значение, созданное через
Symbol(), гарантированно уникально, даже если описание
совпадает.
Symbol('id') === Symbol('id'); // false
Это приводит к тому, что валидация Symbol не может опираться на сравнение значений через равенство описаний — только на тип.
Joi позволяет ограничить допустимые символы через
valid() и allow(). Это особенно полезно, когда
Symbol используется как фиксированный набор ключей.
const A = Symbol('A');
const B = Symbol('B');
const schema = Joi.symbol().valid(A, B);
schema.validate(A); // valid
schema.validate(Symbol('A')); // error (другой Symbol)
Ключевой момент: даже символ с тем же описанием считается другим объектом.
Глобальный реестр символов через Symbol.for() создаёт
переиспользуемые значения, что делает возможной более предсказуемую
валидацию.
const A = Symbol.for('A');
const B = Symbol.for('B');
const schema = Joi.symbol().valid(Symbol.for('A'), Symbol.for('B'));
schema.validate(Symbol.for('A')); // valid
schema.validate(Symbol('A')); // error
Особенность глобального реестра заключается в том, что
Symbol.for() возвращает один и тот же символ для одного
ключа, что делает такие значения стабильными для схем.
Symbol не подлежит автоматическому преобразованию из строк или чисел.
В отличие от string() или number(), Joi не
пытается привести входное значение к Symbol.
const schema = Joi.symbol();
schema.validate('Symbol.for("A")'); // error
schema.validate('A'); // error
Это важно учитывать при работе с внешними источниками данных (JSON, HTTP-запросы), где Symbol не сериализуется напрямую.
Ограничение JSON-формата заключается в отсутствии поддержки Symbol. При попытке сериализации такие значения теряются:
JSON.stringify({ key: Symbol('id') }); // {}
По этой причине схемы Joi, использующие symbol(), обычно
применяются только внутри памяти приложения, а не на границе обмена
данными.
Хотя описание Symbol не влияет на его уникальность, оно часто используется для отладки. Joi не учитывает description при сравнении, но оно может служить дополнительной информацией при проектировании схем.
const schema = Joi.symbol();
const s = Symbol('user_id');
schema.validate(s); // valid
Joi позволяет переопределять сообщения для ошибок валидации Symbol.
const schema = Joi.symbol().messages({
'symbol.base': 'Ожидался Symbol'
});
schema.validate(123); // ошибка с кастомным сообщением
Тип ошибки symbol.base возникает при передаче значения,
не являющегося Symbol.
Symbol часто используется в составе alternatives, когда
допускается несколько типов значений.
const schema = Joi.alternatives().try(
Joi.string(),
Joi.symbol()
);
schema.validate('test'); // valid
schema.validate(Symbol('id')); // valid
Такая модель применяется в случаях, когда идентификатор может приходить в разных формах в зависимости от источника данных.
Symbol может использоваться как ключ объекта, однако Joi валидирует именно значение, а не ключ.
const KEY = Symbol('key');
const schema = Joi.object({
[KEY]: Joi.symbol()
});
Особенность заключается в том, что доступ к таким ключам невозможен через JSON и требует прямого обращения через переменную Symbol.
Работа с Symbol валидацией имеет ряд ограничений:
Symbol.for().Эти особенности делают Joi.symbol() специализированным инструментом для внутренних API и системных структур данных, где требуется строгая типизация и уникальность идентификаторов без внешней сериализации.