Целые и вещественные числа

Валидация числовых значений в Ajv опирается на спецификацию JSON Schema, где числовой тип разделяется на два базовых варианта: целые числа и числа с плавающей точкой. Различие между ними формально задаётся через ключ type, принимающий значения "integer" или "number".

Внутренне Ajv проверяет соответствие значений типу JavaScript number, однако логика валидации учитывает дополнительные ограничения схемы, накладываемые JSON Schema, включая диапазоны, кратность и строгие типовые проверки.


Целые числа (integer)

Тип "integer" в JSON Schema описывает числа без дробной части. В контексте JavaScript это означает проверку на отсутствие остатка от деления на 1.

В Ajv целочисленная проверка реализуется через проверку Number.isInteger().

Пример схемы:

{
  "type": "integer"
}

Такая схема допускает значения:

  • 0
  • 42
  • -15

И отклоняет:

  • 3.14
  • -0.5
  • “10” (если не включено приведение типов)

Особенность заключается в том, что JavaScript не имеет отдельного целочисленного типа, поэтому различие между integer и number носит логический, а не физический характер.

Дополнительно к типу могут применяться ограничения:

{
  "type": "integer",
  "minimum": 0,
  "maximum": 100
}

Такая схема задаёт диапазон допустимых целых значений от 0 до 100 включительно.


Вещественные числа (number)

Тип "number" включает все числовые значения JavaScript, включая целые и дробные. Проверка осуществляется без требования целочисленности.

Пример схемы:

{
  "type": "number"
}

Допустимые значения:

  • 10
  • 3.14
  • -0.001
  • 2e10

При этом важно учитывать, что все числа в JavaScript представлены в формате IEEE 754 double precision, что приводит к особенностям округления и точности.

Ajv не исправляет и не нормализует такие значения, а работает с тем, что передано интерпретатором.


Ограничения диапазонов значений

Для числовых типов в Ajv применяются стандартные ключи JSON Schema:

  • minimum
  • maximum
  • exclusiveMinimum
  • exclusiveMaximum

Пример:

{
  "type": "number",
  "minimum": 0,
  "maximum": 1
}

Эта схема ограничивает значения интервалом [0, 1].

Использование строгих границ:

{
  "type": "number",
  "exclusiveMinimum": 0,
  "exclusiveMaximum": 1
}

В зависимости от версии JSON Schema и настроек Ajv, exclusiveMinimum и exclusiveMaximum могут задаваться как числа или как объекты:

{
  "type": "number",
  "exclusiveMinimum": {
    "value": 0
  }
}

Ajv учитывает версию спецификации (draft-07, 2019-09 и новее), что влияет на интерпретацию этих полей.


Шаг дискретизации multipleOf

Ключ multipleOf задаёт кратность числового значения. Проверка выполняется через деление числа на заданный множитель с учётом погрешности floating point.

Пример:

{
  "type": "number",
  "multipleOf": 0.5
}

Допустимые значения:

  • 0
  • 0.5
  • 1.0
  • 1.5

Недопустимые:

  • 0.3
  • 1.2 (с учётом ошибок округления может потребоваться осторожность)

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


Особенности чисел в JavaScript и влияние на Ajv

Все числовые значения в JavaScript представлены как IEEE 754 double precision floating point. Это приводит к ряду эффектов:

  • 0.1 + 0.2 ≠ 0.3 в точном сравнении
  • возможны погрешности при делении и умножении
  • большие числа могут терять точность

Ajv не выполняет арифметическую коррекцию значений, а полагается на результат вычислений JavaScript.

При использовании multipleOf, minimum, maximum сравнения выполняются через стандартные операции <, >, === с учётом особенностей IEEE 754.


Приведение типов (coercion)

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

При включении:

const ajv = new Ajv({ coerceTypes: true });

возможны преобразования:

  • "10"10
  • "3.14"3.14

При этом для integer выполняется дополнительная проверка после преобразования:

  • "10" → 10 → допустимо
  • "10.5" → 10.5 → отклоняется при типе integer

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


Строгая проверка и поведение ошибок

Ajv формирует структурированные ошибки при нарушении числовых ограничений. Ошибки содержат:

  • путь к полю (instancePath)
  • тип нарушения (keyword)
  • ожидаемое значение
  • фактическое значение

Пример типичной ошибки:

{
  "instancePath": "/age",
  "keyword": "minimum",
  "message": "should be >= 18",
  "params": {
    "comparison": ">=",
    "limit": 18
  }
}

Для числовых типов возможны ошибки:

  • type (не число или не integer)
  • minimum
  • maximum
  • multipleOf

Примеры комплексных схем

Схема с целым числом и диапазоном:

{
  "type": "object",
  "properties": {
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 120
    }
  }
}

Схема с вещественным числом и точностью:

{
  "type": "object",
  "properties": {
    "temperature": {
      "type": "number",
      "minimum": -273.15,
      "multipleOf": 0.01
    }
  }
}

Схема с автоматическим приведением типов:

{
  "type": "object",
  "properties": {
    "price": {
      "type": "number"
    }
  },
  "required": ["price"]
}

При включённом coerceTypes значение "19.99" будет интерпретировано как число.


Взаимодействие integer и number в одной схеме

Допускается комбинирование типов через массив:

{
  "type": ["integer", "number"]
}

В такой конструкции значение проходит валидацию, если оно является числом, независимо от наличия дробной части. Однако семантически это редко используется, поскольку integer уже является подмножеством number.

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