Symbol

Валидация значений типа Symbol в JavaScript требует отдельного подхода, поскольку Symbol не является строкой, числом или объектом в привычном смысле и обладает уникальной семантикой идентичности. В библиотеке Joi для этого предусмотрен специализированный валидатор Joi.symbol().


Базовая схема 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

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()

Глобальный реестр символов через 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

Ограничение JSON-формата заключается в отсутствии поддержки Symbol. При попытке сериализации такие значения теряются:

JSON.stringify({ key: Symbol('id') }); // {}

По этой причине схемы Joi, использующие symbol(), обычно применяются только внутри памяти приложения, а не на границе обмена данными.


Использование описаний 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 валидацией имеет ряд ограничений:

  • невозможность сериализации в JSON;
  • невозможность восстановления Symbol из строкового представления;
  • строгая идентичность без учёта описания;
  • зависимость от контекста исполнения при использовании Symbol.for().

Эти особенности делают Joi.symbol() специализированным инструментом для внутренних API и системных структур данных, где требуется строгая типизация и уникальность идентификаторов без внешней сериализации.