Ваши типы исчезают в рантайме. t12n возвращает проверку.

Чужие данные приходят отовсюду — ответ API, который вы кладёте в типизированную переменную, аргумент функции, JSON.parse, localStorage. t12n читает ваш тип и прямо там, в момент прихода данных, вставляет настоящую проверку. Без схем, без вызовов validate() — а в live-режиме продолжает следить за значением всю его жизнь.

$ npm i t12n

v0.1 · beta ~3 KB/0 зависимостей/~8× Zod

user.ts

type User = {
  id: string
  email: string
  age: number
}

// прямо из сети — без проверки
const user: User =
  await fetch('/api/me').then(r => r.json())
// валидатор t12n, скомпилирован из User
function checkUser(v) {
  if (typeof v.id    !== 'string') fail('id')
  if (typeof v.email !== 'string') fail('email')
  if (typeof v.age   !== 'number') fail('age')
  return v
}

// граница теперь под охраной
const user = checkUser(
  await fetch('/api/me').then(r => r.json())
)
type Order = {
  id: string
  total: number
  status: 'paid' | 'pending'
}

// восстановлено из localStorage — без проверки
const order: Order =
  JSON.parse(localStorage.cart)
// валидатор t12n, скомпилирован из Order
function checkOrder(v) {
  if (typeof v.id    !== 'string') fail('id')
  if (typeof v.total !== 'number') fail('total')
  if (v.status !== 'paid' &&
      v.status !== 'pending')      fail('status')
  return v
}

const order = checkOrder(
  JSON.parse(localStorage.cart)
)
type Message = {
  kind: string
  data: number[]
}

// пришло через postMessage — без проверки
const msg: Message = event.data
// валидатор t12n, скомпилирован из Message
function checkMessage(v) {
  if (typeof v.kind !== 'string') fail('kind')
  if (!Array.isArray(v.data))     fail('data')
  for (const n of v.data)
    if (typeof n !== 'number')    fail('data[]')
  return v
}

const msg = checkMessage(event.data)

01 разрыв

Тип — это обещание, которое
рантайм не проверяет.

без t12n

const user: User = await fetch('/me')
  .then(r => r.json())

// API прислал { id: 1, email: null }

// …40 строк спустя, без следа к причине:
user.email.toLowerCase()
// 💥 Cannot read … of null

с t12n

const user: User = await fetch('/me')
  .then(r => r.json())

// проверка уже здесь,
// сгенерирована из типа User.

// ✗ поймано на этой строке: поле,
//   ожидаемый тип и что пришло.

02 как это работает

Вы пишете тип.
Проверку пишет сборка.

1

Поставьте тип

Обычный тип на границе или маркер Check<T> там, где нужно явно.

2

Плагин его читает

На сборке разрешает реальный тип через TS-чекер — дженерики, union'ы, Date и всё — в схему.

3

Валидатор в бандле

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

03 везде, где входят данные

Любая граница. Любой фреймворк.

Границы, за которыми следит

fetch().json()JSON.parse()localStoragepostMessageas any / unknownпараметры функцийвозвраты функцийref / useState

Работает с

React
Vue
Svelte
Solid
Next.js
Nuxt
Astro
VitewebpackRollupesbuildRspack

04 скорость

Почти как проверка,
написанная руками.

Статический доступ к полям, без поиска по схеме, без приведения типов — и ноль аллокаций, когда данные уже совпадают. Примерно в 8× быстрее Zod.

Руками потолок скорости~775k/s
t12n вшит в бандл~640k/s
Zod v4~75k/s
~3 KBрантайм в gzip
0рантайм-зависимостей
0приведений типов — никогда
CSPбезопасно — без eval в рантайме

05 когда падает

Когда падает —
сразу видно где.

Падает на той строке, куда пришли данные, — и говорит файл и строку, поле, какой тип ждали и что реально пришло. А configure() решает, что делать с провалом: в разработке — бросать ошибку, в проде — тихо слать в телеметрию.

  • файл и строка, где сработала проверка
  • из какой границы пришло значение (fetch, JSON.parse…)
  • путь до поля, что ждали и что получили

06 за пределами границы

Проверить один раз — или
на всю жизнь объекта.

Обычная проверка срабатывает один раз — на границе. Включите live-режим, и t12n вернёт Proxy, который держит тип под контролем постоянно: неправильная запись даже сотней строк позже тоже поймается.

Настройка — около пяти минут.

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