Частичное применение

Частичное применение в Superstruct — это подход, при котором базовые структуры и валидаторы превращаются в переиспользуемые фабрики с заранее заданными параметрами. Вместо того чтобы каждый раз заново описывать структуру данных, создаётся функция-обёртка, фиксирующая часть конфигурации и возвращающая готовый или полуготовый валидатор.

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

Типичная структура:

import { string, number, object } from 'superstruct'

const User = object({
  name: string(),
  age: number(),
})

Каждая функция здесь — это независимый строительный блок. Именно эта модульность делает возможным частичное применение: можно заранее зафиксировать часть поведения и переиспользовать его в разных контекстах.

Суть частичного применения в контексте структур

Частичное применение означает создание новой функции на основе существующей, где часть аргументов уже «заполнена». В контексте Superstruct это чаще всего проявляется в трёх формах:

  • фиксация правил валидации;
  • создание фабрик структур;
  • преднастроенные обёртки над базовыми типами.

Пример логики вне контекста Superstruct:

const add = (a) => (b) => a + b

const add10 = add(10)

add10(5) // 15

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

Создание фабрик структур

Один из наиболее практичных способов частичного применения — создание фабрик, возвращающих готовые структуры.

import { object, string, number } from 'superstruct'

const createUserStruct = (extraFields = {}) => {
  return object({
    name: string(),
    age: number(),
    ...extraFields,
  })
}

const AdminUser = createUserStruct({
  role: string(),
})

Здесь createUserStruct фиксирует базовую структуру пользователя, а дополнительные поля добавляются поверх. Это упрощает масштабирование схем в больших проектах.

Частичное применение через обёртки над примитивами

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

import { string, size } from 'superstruct'

const emailString = () =>
  size(string(), 5, 255)

const usernameString = () =>
  size(string(), 3, 30)

Такие функции фактически являются частично применёнными валидаторами: базовый string() фиксируется и дополняется ограничениями.

Переиспользуемые правила как строительные блоки

Частичное применение особенно полезно, когда в проекте появляется повторяющаяся логика проверки данных.

import { string, refine } from 'superstruct'

const trimmedString = () =>
  refine(string(), 'trimmedString', (value) => {
    return value === value.trim()
  })

const nonEmptyString = () =>
  refine(string(), 'nonEmptyString', (value) => {
    return value.length > 0
  })

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

Композиция частично применённых структур

Сила подхода раскрывается при комбинировании заранее подготовленных валидаторов.

import { object } from 'superstruct'

const BaseUser = object({
  name: trimmedString(),
  email: emailString(),
})

const ExtendedUser = object({
  ...BaseUser.schema,
  role: usernameString(),
})

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

Частичное применение и параметризация валидаторов

Иногда требуется создать валидатор, зависящий от внешнего параметра. Например, ограничение длины строки.

import { string, size } from 'superstruct'

const createSizedString = (min, max) => () =>
  size(string(), min, max)

const shortText = createSizedString(0, 50)
const longText = createSizedString(0, 500)

Функция createSizedString фиксирует логику проверки, а параметры задаются один раз при создании.

Динамические фабрики структур

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

import { object, number, string, optional } from 'superstruct'

const createEntityStruct = ({ withId = true, withTimestamps = false }) => {
  const base = {}

  if (withId) {
    base.id = number()
  }

  if (withTimestamps) {
    base.createdAt = string()
    base.updatedAt = optional(string())
  }

  return object(base)
}

const Post = createEntityStruct({ withId: true, withTimestamps: true })

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

Частичное применение и типизация логики валидации

Хотя Superstruct не является системой типов TypeScript, частичное применение помогает синхронизировать логику валидации с типами данных на уровне архитектуры.

import { number, refine } from 'superstruct'

const positiveNumber = () =>
  refine(number(), 'positiveNumber', (v) => v > 0)

const percentage = () =>
  refine(number(), 'percentage', (v) => v >= 0 && v <= 100)

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

Организация слоя валидаторов в проекте

В больших приложениях частичное применение становится основой архитектуры слоя валидации. Обычно выделяются:

  • базовые примитивы;
  • частично применённые валидаторы;
  • фабрики структур;
  • композиции доменных моделей.

Пример структуры каталога:

validation/
  primitives/
    stringValidators.js
    numberValidators.js
  factories/
    createUserStruct.js
    createEntityStruct.js
  models/
    user.js
    post.js

Такой подход снижает связанность кода и упрощает расширение схем.

Повторное использование через композицию функций

Частичное применение тесно связано с функциональной композицией. Вместо того чтобы создавать монолитные структуры, система строится из маленьких переиспользуемых элементов.

const compose =
  (...fns) =>
  (x) =>
    fns.reduce((acc, fn) => fn(acc), x)

Хотя Superstruct не требует такой композиции напрямую, архитектурно она хорошо сочетается с частично применёнными валидаторами.

Ограничения подхода

Несмотря на гибкость, частичное применение в схемах валидации требует аккуратности:

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

Поэтому чаще всего используется баланс между явным описанием структур и переиспользуемыми фабриками.

Практическая роль в масштабируемых системах

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

  • API-схемами;
  • формами;
  • доменными моделями;
  • интеграциями с внешними сервисами.

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