Кортежи: tuple schema

Кортежи в Yup представляют собой специализированную форму описания структур данных фиксированной длины, где каждый элемент имеет строго определённый тип и позиционное значение. В отличие от обычных массивов, где элементы одного типа или смешанного типа описываются единым набором правил, кортеж задаёт жёсткую схему для каждой позиции, формируя предсказуемую и детерминированную структуру данных.

В библиотеке Yup кортежи реализуются через yup.tuple, принимающий массив схем, каждая из которых соответствует конкретному индексу. Это позволяет описывать данные, где порядок элементов критически важен и несёт смысловую нагрузку.

Кортеж задаётся как последовательность схем:

import * as yup from 'yup';

const schema = yup.tuple([
  yup.string(),
  yup.number(),
  yup.boolean()
]);

В данном случае ожидается массив ровно из трёх элементов:

  • первый элемент — строка,
  • второй — число,
  • третий — булево значение.

Любое отклонение от этой структуры приводит к ошибке валидации.

Отличие кортежа от массива

Обычный массив в Yup описывается через yup.array().of(schema), где все элементы подчиняются одной схеме:

const arraySchema = yup.array().of(yup.string());

Кортеж же фиксирует структуру по позициям:

const tupleSchema = yup.tuple([
  yup.string(),
  yup.string(),
  yup.string()
]);

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

Валидация кортежей

При валидации Yup проверяет каждый элемент по соответствующей позиции:

const schema = yup.tuple([
  yup.string().required(),
  yup.number().min(0),
  yup.boolean()
]);

schema.validate(['test', 42, true]);

Если хотя бы один элемент не соответствует своей схеме, валидация завершается ошибкой. Например:

schema.validate(['test', -5, true]);

Во втором элементе нарушено условие min(0), что делает весь кортеж невалидным.

Жёсткая фиксированная длина

Кортеж в Yup по умолчанию предполагает фиксированную длину. Это означает, что:

  • недостаток элементов приводит к ошибке,
  • лишние элементы также считаются нарушением структуры.
const schema = yup.tuple([
  yup.string(),
  yup.number()
]);

schema.validate(['a']);       // ошибка: не хватает элемента
schema.validate(['a', 1, 2]); // ошибка: лишний элемент

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

Необязательные элементы

Несмотря на строгую природу, отдельные позиции могут быть сделаны необязательными:

const schema = yup.tuple([
  yup.string().required(),
  yup.number().notRequired(),
  yup.boolean().notRequired()
]);

В этом случае допускается undefined на позициях, где указано notRequired.

Пример допустимого значения:

['hello']
['hello', 10]
['hello', 10, false]

Nullable значения

Каждый элемент кортежа может принимать null, если явно разрешить это:

const schema = yup.tuple([
  yup.string().nullable(),
  yup.number().nullable()
]);

Теперь допустимы значения:

['text', null]
[null, 5]

Типизация и TypeScript

Кортежи Yup тесно интегрируются с TypeScript, позволяя выводить строгие типы:

const schema = yup.tuple([
  yup.string(),
  yup.number(),
  yup.boolean()
]);

type Result = yup.InferType<typeof schema>;
// Result: [string, number, boolean]

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

Вложенные кортежи

Кортежи могут быть вложенными, формируя сложные структуры:

const schema = yup.tuple([
  yup.string(),
  yup.tuple([
    yup.number(),
    yup.boolean()
  ])
]);

Пример валидного значения:

['id', [42, true]]

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

Трансформации внутри кортежей

Каждый элемент кортежа может иметь собственные трансформации:

const schema = yup.tuple([
  yup.string().trim(),
  yup.number().transform(value => Number(value))
]);

В этом случае входные данные нормализуются до прохождения финальной проверки.

Поведение при частичной валидации

При использовании методов частичной проверки (validateAt) можно проверять конкретную позицию кортежа:

schema.validateAt('[1]', ['test', 'not-a-number']);

Это позволяет изолировать ошибки в конкретных элементах структуры.

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

Кортежи часто применяются для описания фиксированных структур конфигураций, например:

const endpointSchema = yup.tuple([
  yup.string(),  // URL
  yup.string(),  // метод
  yup.number()   // таймаут
]);

Пример данных:

['/api/users', 'GET', 5000]

Каждая позиция здесь имеет строго определённую роль, и нарушение порядка полностью меняет смысл данных.

Сравнение с альтернативами

В некоторых случаях кортеж можно заменить объектом:

{
  url: string,
  method: string,
  timeout: number
}

Однако кортежи предпочтительны, когда:

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

Ошибки валидации

Ошибки в кортежах Yup всегда привязаны к индексу элемента:

  • tuple[0] — ошибка первого элемента,
  • tuple[1] — ошибка второго и так далее.

Это упрощает отладку сложных структур, так как источник ошибки точно локализован.

Комбинация с массивами и объектами

Кортежи могут использоваться внутри более сложных схем:

const schema = yup.object({
  meta: yup.tuple([
    yup.string(),
    yup.number()
  ]),
  tags: yup.array(yup.string())
});

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

Особенности сериализации

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

Ограничения кортежей

Кортежи в Yup имеют ряд ограничений:

  • фиксированная длина затрудняет работу с динамическими данными,
  • сложность расширения схемы без изменения индексов,
  • повышенная чувствительность к порядку элементов.

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