Запрет дополнительных свойств

При валидации JSON-данных в схемах JSON Schema одной из ключевых задач является контроль структуры объекта. Помимо проверки обязательных полей и типов значений, важно ограничивать появление неожиданных свойств, которые не предусмотрены контрактом данных. В Ajv это поведение реализуется через механизм запрета дополнительных свойств.

В спецификации JSON Schema за контроль «лишних» полей отвечает ключ additionalProperties. Его поведение зависит от значения:

  • true — разрешены любые дополнительные поля
  • false — запрещены все свойства, не описанные в properties или patternProperties
  • схема (object schema) — дополнительные свойства должны соответствовать указанной схеме

Простейший пример ограничения:

{
  "type": "object",
  "properties": {
    "id": { "type": "number" },
    "name": { "type": "string" }
  },
  "additionalProperties": false
}

При такой схеме объект:

{
  "id": 1,
  "name": "Alice",
  "role": "admin"
}

будет считаться невалидным из-за поля role.

Поведение additionalProperties в Ajv

Библиотека Ajv строго следует спецификации JSON Schema, но добавляет собственные режимы строгой проверки и оптимизации. При использовании additionalProperties: false выполняется проверка всех ключей объекта на соответствие описанным в схеме.

Внутренне Ajv:

  • собирает список разрешённых свойств из properties
  • учитывает шаблонные свойства из patternProperties
  • проверяет каждое поле объекта
  • фиксирует ошибки для «лишних» ключей

Типичная ошибка выглядит так:

data should NOT have additional properties 'role'

strict mode и контроль структуры

В современных версиях Ajv важную роль играет режим strict, который усиливает контроль над схемами. В контексте запрета дополнительных свойств он позволяет выявлять ошибки проектирования схем:

  • использование неизвестных ключей в схеме
  • конфликтующие определения
  • неявные расширения объекта

При включённом строгом режиме Ajv предупреждает о потенциально некорректных конструкциях, связанных с additionalProperties, особенно если они могут привести к неоднозначной валидации.

difference между additionalProperties и unevaluatedProperties

Начиная с JSON Schema Draft 2019-09, появился более универсальный механизм — unevaluatedProperties. В Ajv он также поддерживается и часто используется в сложных композициях схем.

Различие:

  • additionalProperties проверяет только свойства, не описанные напрямую в properties и patternProperties
  • unevaluatedProperties учитывает все свойства, которые не были «поглощены» другими частями схемы (например, allOf, oneOf, if/then/else)

Пример:

{
  "type": "object",
  "properties": {
    "a": { "type": "string" }
  },
  "allOf": [
    {
      "properties": {
        "b": { "type": "number" }
      }
    }
  ],
  "unevaluatedProperties": false
}

В такой конструкции additionalProperties уже недостаточно, поскольку свойства могут появляться через композиции.

Поведение Ajv при вложенных объектах

Запрет дополнительных свойств распространяется рекурсивно на вложенные структуры. Например:

{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "id": { "type": "number" }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}

Здесь ограничения действуют на двух уровнях:

  • внешний объект не допускает лишних ключей
  • объект user также строго ограничен полем id

Ajv обрабатывает такие схемы по дереву, формируя отдельные проверки для каждого уровня вложенности.

Режим removeAdditional

Помимо простого запрета дополнительных свойств Ajv предоставляет механизм автоматического удаления лишних полей.

Опция removeAdditional может принимать значения:

  • false — ничего не удаляется
  • true — удаляются все дополнительные свойства
  • "all" — удаляются даже свойства, не описанные в properties, включая вложенные структуры

Пример использования:

const ajv = new Ajv({ removeAdditional: "all" })

Схема:

{
  "type": "object",
  "properties": {
    "id": { "type": "number" }
  },
  "additionalProperties": false
}

Вход:

{
  "id": 1,
  "temp": 123
}

Результат после валидации:

{
  "id": 1
}

Удаление происходит до завершения валидации, что влияет на итоговый объект.

Влияние patternProperties

patternProperties изменяет поведение запрета дополнительных свойств, так как расширяет набор допустимых ключей через регулярные выражения.

Пример:

{
  "type": "object",
  "patternProperties": {
    "^x-": { "type": "string" }
  },
  "additionalProperties": false
}

Здесь разрешены все свойства, начинающиеся с x-, например:

{
  "x-meta": "value",
  "x-id": "123"
}

При этом любые другие ключи будут отклонены.

Ajv при валидации объединяет:

  • properties
  • patternProperties
  • затем применяет ограничение additionalProperties

Типичные ошибки при использовании additionalProperties

В реальных схемах часто встречаются следующие проблемы:

Конфликт с allOf

{
  "allOf": [
    {
      "properties": {
        "a": { "type": "string" }
      }
    },
    {
      "additionalProperties": false
    }
  ]
}

В таком случае может возникнуть ситуация, когда свойства, определённые в разных частях allOf, не учитываются корректно без использования unevaluatedProperties.

Неполное описание объекта

Если забыть определить properties, но указать additionalProperties: false, объект будет полностью запрещён:

{
  "type": "object",
  "additionalProperties": false
}

Любой ключ приведёт к ошибке, включая пустой объект с динамическими расширениями.

Неожиданные поля из API

При интеграции внешних API часто появляются поля, не описанные в схеме. В режиме строгого запрета это приводит к массовым ошибкам валидации, особенно если API возвращает метаданные или служебные поля.

Производительность проверки

Ajv оптимизирует проверку дополнительных свойств:

  • заранее компилирует список разрешённых ключей
  • использует быстрые lookup-структуры (Set/Map)
  • минимизирует повторные проверки в рекурсивных схемах

Тем не менее, при глубоко вложенных объектах с большим числом свойств проверка additionalProperties: false может становиться заметной по стоимости, особенно в сочетании с patternProperties.

Практическая модель строгих контрактов

Использование запрета дополнительных свойств формирует строгий контракт данных:

  • фиксированная структура объекта
  • отсутствие «скрытых» полей
  • предсказуемая сериализация
  • упрощённая валидация на уровне API

Ajv в таком режиме используется как инструмент обеспечения жёсткой схемы данных, где любое отклонение структуры считается ошибкой, а не игнорируется.

Взаимодействие с TypeScript-подобными моделями

В схемах, которые отражают типы из TypeScript, additionalProperties: false эквивалентен индексной сигнатуре с запретом расширений. Это особенно важно при генерации схем из типов, где требуется сохранить строгую структуру без лишних ключей.

Поведение ошибок и диагностика

Ajv возвращает структурированные ошибки, содержащие:

  • путь до свойства (instancePath)
  • ключ, вызвавший ошибку
  • описание нарушения

Пример:

{
  "instancePath": "",
  "message": "must NOT have additional properties",
  "params": {
    "additionalProperty": "role"
  }
}

Это позволяет точно определить источник нарушения структуры.

Итоговое поведение механизма

Запрет дополнительных свойств в Ajv формирует жёсткую модель валидации, в которой:

  • каждый ключ объекта должен быть явно описан
  • любые отклонения фиксируются как ошибка
  • вложенные структуры проверяются рекурсивно
  • возможна автоматическая очистка данных
  • поддерживаются современные механизмы вроде unevaluatedProperties

Такой подход делает схемы предсказуемыми и строгими, особенно в системах, где важна точность контракта данных между компонентами.