Never

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


Семантика и назначение

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

Ключевые свойства:

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

Фактически never — это декларация запрета на любые данные.


Базовое поведение

При проверке значения через never всегда возникает ошибка.

import { assert, never } from 'superstruct'

assert(123, never) // ошибка
assert('text', never) // ошибка
assert(null, never) // ошибка
assert(undefined, never) // ошибка
assert({}, never) // ошибка

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


Использование в схемах

Запрещённые поля объектов

Один из практических сценариев — явное запрещение определённых ключей в объектных структурах.

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

const User = object({
  id: string(),
  password: never(),
})

В этом случае:

  • id допустим и должен быть строкой
  • password полностью запрещён

Любое наличие password в объекте делает структуру невалидной.


Моделирование недостижимых состояний

В TypeScript и аналогичных системах тип never используется для описания невозможных веток. Superstruct поддерживает аналогичную семантику на уровне данных.

Пример:

const Response = object({
  status: string(),
  data: never(),
})

Такой объект невозможно корректно создать с полем data.


Комбинация с union-структурами

never часто используется внутри объединений для исключения вариантов.

import { union, string, number, never } from 'superstruct'

const Value = union([string(), number(), never()])

С точки зрения логики:

  • string допустим
  • number допустим
  • never не добавляет новых возможностей

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


Использование для строгих контрактов API

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

const Request = object({
  query: string(),
  debug: never(),
  internalToken: never(),
})

Любая попытка передать debug или internalToken приводит к ошибке валидации.

Это позволяет:

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

Отличие от optional и null

Важно различать never и другие ограничения:

optional

Поле может отсутствовать, но если присутствует — валидируется.

nullable

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

never

Поле не может существовать вообще.

import { optional, nullable, never, string, object } from 'superstruct'

const Example = object({
  a: optional(string()),
  b: nullable(string()),
  c: never(),
})

Поведение:

  • a можно не указывать
  • b можно сделать null
  • c запрещено полностью

Роль в типизации и статическом анализе

В экосистеме TypeScript never означает отсутствие значений. Superstruct переносит эту концепцию на уровень runtime-валидации.

Это позволяет:

  • синхронизировать runtime и compile-time логику
  • предотвращать рассинхронизацию типов и данных
  • усиливать гарантию корректности схем

Применение в дискриминированных объединениях

never часто используется для отключения определённых вариантов в union-структурах.

import { union, object, literal, never } from 'superstruct'

const Shape = union([
  object({
    type: literal('circle'),
    radius: number(),
  }),
  object({
    type: literal('square'),
    side: number(),
  }),
  object({
    type: literal('triangle'),
    side: number(),
  }),
  object({
    type: literal('broken'),
    data: never(),
  }),
])

Вариант broken становится недостижимым в корректных данных и служит для:

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

Поведение при кастомных проверках

Если never используется внутри validate, он всегда возвращает ошибку независимо от контекста.

import { validate, never } from 'superstruct'

const [error] = validate('anything', never)

console.log(error) // всегда существует

Архитектурное значение

Использование never в схемах — это не просто запрет, а способ выразить структурную невозможность данных.

Он полезен в ситуациях, когда:

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

Сочетание с трансформациями

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

import { assign, object, string, never } from 'superstruct'

const Struct = object({
  name: string(),
  removed: never(),
})

const Safe = assign(Struct, Struct)

Любая попытка трансформации с removed прерывает процесс валидации.


Роль в документации схем

Хотя never не допускает данных, он полезен как часть документации схемы:

  • фиксирует запрет на уровне кода
  • предотвращает расширение API в нежелательных направлениях
  • делает ограничения явными и проверяемыми

Итоговая характеристика поведения

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