Skip to content
Published on

TypeScriptデータ検証 2026 — Zod·Valibot·ArkType·TypeBox·Effect Schema徹底比較(AIツール呼び出し時代のランタイム検証)

Authors

プロローグ — すべてのAPI境界がスキーマを要求する時代

2022年のTypeScriptで「ランタイム検証は何を使う?」と聞けば答えはほぼ一つだった。Zod。その前はJoiYup、または手書きの型ガード。

2026年の同じ問いには候補が五つある。その間に多くのことが起きた。

  • tRPCが標準になった。 すべての手続き入力にZodスキーマを差し込むパターンがフルスタックTSの基本文法になった。スキーマ = API境界。
  • React Hook Form + zodResolverパターンがフォームのデファクト標準。 フォーム検証 = 同じスキーマ。
  • AIツール呼び出し(function calling, tool use)がJSON Schemaを強制。 OpenAI・Anthropic・Geminiすべてがツール定義をJSON Schemaで受ける。TSスキーマからJSON Schemaを出せないと困ることになった。
  • OpenAPI自動生成が再び熱い。 Hono・tRPC-openapi・@asteasolutions/zod-to-openapi・TypeBoxの組み込みインターロップ。1スキーマでクライアント・サーバー・ドキュメントが同時に出る。
  • バンドルサイズ = 金。 エッジランタイム・Cloudflare Workersに乗るコードが増えた。Zod 1MBの時代は過ぎたが、KB単位で計算する陣営が育った。

この流れが五つの陣営を作った。

  • Zod 4 — デファクト標準。4.xでツリーシェイキング・パーサー再記述で弱点を正面から潰した。
  • Valibot — 関数型モジュラーAPI。使った分しかバンドルに入らない。フロントエンドフォーム・エッジで強い。
  • ArkType — TS文字列をスキーマに。type({ email: 'string.email' })のような形式。TS推論との100%整合が武器。
  • TypeBox — 書いたスキーマがそのままJSON Schema。Fastify・OpenAPI・AIツール呼び出しと直結。
  • Effect Schema — Effectエコシステムの検証ツール。型付きエラー・合成可能な変換・双方向エンコーディングが武器。
  • Yup / Joi / Superstruct — 生きているが新規採用は減り続ける。

この記事は2026年5月時点で、これらのライブラリを直接比較する。バンドル・推論・JSON Schema・非同期・OpenAPI・ランタイムコスト・エコシステムの7軸で。そして同じUserスキーマを5ライブラリで横並びに書く。


1章 · 風景 — データ検証ライブラリのマップ

まずは分類から。すべてが同じ位置にあるわけではない。

分類代表ツールひとこと要約
オブジェクトビルダー(チェーン)Zod, Yup, Joiz.string().email()スタイル。最も馴染みやすい。
モジュラー関数Valibotstring([email()])。ツリーシェイクに優しい。
TS文法をスキーマにArkTypeTS型表現をそのままランタイムに。
JSON SchemaネイティブTypeBox書いたものがJSON Schemaそのもの。
関数型 + EffectEffect Schema型付きエラー、双方向エンコーディング。
軽量アサーションSuperstruct, io-ts小さく合成可能、やや古典的なスタイル。

この記事の焦点は太字に分かれる五つ — Zod・Valibot・ArkType・TypeBox・Effect Schema。Yup・Joi・Superstructは11章で短く扱う。

なぜこの五つか

  • Zod 4 — 新規TypeScriptプロジェクトのデフォルト。tRPC・Next.js・React Hook Formどこでも登場。2024年発表のZod 4が2025〜2026に安定化してツリーシェイキングの弱点が大きく減った。
  • Valibot — 2024年から本格成長。関数単位importで未使用コードがバンドルから落ちる。フロントエンド・エッジでシェアを急速に拡大。
  • ArkType — TS表現をそのまま受ける発想の転換が魅力。v2安定化以降、新規プロジェクトの一筋。
  • TypeBox — Fastify推奨、AJVと組み合わせると最速ランタイムの一つ。LLMにJSON Schemaを直送する場合の第一候補。
  • Effect Schema — Effectフルスタックを行くチームの検証ツール。Effectを使わないなら採用理由は薄い。

2章 · 7軸比較マトリクス

詳細に入る前に全体像。

Zod 4ValibotArkTypeTypeBoxEffect Schema
バンドル(コア)非常に小さい(モジュラー)非常に小さい大(Effect本体含む)
TS推論品質非常に良い非常に良い最高(TS表現そのまま)良い非常に良い
JSON Schemaエクスポート外部パッケージ必要外部パッケージ必要組み込み組み込み(定義自体がJSON Schema)組み込み
非同期検証ありあり部分的なし(同期中心)あり
OpenAPIエクスポートzod-to-openapiなど@valibot/to-json-schema組み込み組み込み(Fastify·AJV)@effect/schema-openapi
ランタイムコスト速い(v4で大幅改善)非常に速い速い最速(AJVコンパイル時)中〜速
エコシステム最大急成長API/Fastify陣営Effectユーザー

この表だけで決めてはいけない。次章から各ツールを正確に何ができて何ができないかを抑える。


3章 · Zod — 標準になった理由とZod 4の反撃

Zodは2020年登場以来、TypeScript検証のデファクト標準になった。理由は三つ。

  1. チェーンAPIの馴染みやすさz.string().email().min(5)一行で意図が伝わる。Joi・Yupから来た人にとって学習コストはほぼゼロ。
  2. z.infer型推論 — スキーマからTS型を自動導出。スキーマ = 型の真実の源泉。
  3. エコシステム爆発 — tRPC・@hookform/resolvers・OpenAI SDK・LangChain・Next.js Server Actions等が全部Zodをファーストクラスで受ける。

Zod 3時代の弱点

それでもZodは二つの批判を長く受けた。

  • バンドルサイズ — チェーンAPIの性質上インデックス全体が一度に取り込まれるため、ツリーシェイキングが弱い。素のz.string()だけでも数十KB。
  • ランタイムコスト — 深いオブジェクトのパースが意外に重い。ホットパス(リクエストあたり数千回)で見えた。

Zod 4が変えたもの

Zod 4は2024年10月にベータ公開、2025年に本格GA。大きな変更が三つ。

  • ツリーシェイカブルビルド — 未使用バリデータがバンドルから落ちる。import { z } from 'zod'はそのままで、結果のバンドルが小さくなる。
  • パーサー再記述 — 内部を単純化しホットパスを最適化。公式ベンチマークではv3に対し意味のある速度向上(数値はリリースノート参照)。
  • ビルド分割zodコアとzod/v4、ミニビルド(zod/v4-mini)が分かれた。後者はValibot級の超小型バンドルを狙う。

Zodで同じUserスキーマ

import { z } from 'zod'

const User = z.object({
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  name: z.string().min(1),
})

type User = z.infer<typeof User>

const parsed = User.parse({ email: 'foo@example.com', age: 30, name: 'Foo' })
//    ^ parsed: User

safeParseでエラーを値として受け取る。

const result = User.safeParse(unknown)
if (!result.success) {
  // result.error is a ZodError; pretty issues at result.error.issues
  console.error(result.error.flatten())
}

Zodの強み(2026)

  • 最大のエコシステム。新規ライブラリ・SDKがほぼ自動でZodアダプタを提供。
  • 4.xでバンドル・速度の弱点が大幅に縮小。
  • 非同期検証(z.string().refine(async ...))対応が滑らか。
  • 学習リソースが圧倒的。

Zodの弱み(2026)

  • 絶対値のバンドルは依然Valibotより大きい。エッジワーカーでKBを争う場面では候補外。
  • z.discriminatedUnion等の一部APIに学習コスト。
  • JSON Schemaエクスポートは外部パッケージ(@asteasolutions/zod-to-openapi等)。ネイティブ対応は部分的。

4章 · Valibot — 関数型モジュラーでバンドルを削る

Valibotは2023年末〜2024年に爆発的に成長したライブラリ。コア哲学は一つ。

「基本を関数で書く。使わないコードはバンドルにない。」

Zodがz.string().email().min(5)ならValibotは関数合成。

import * as v from 'valibot'

const User = v.object({
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150)),
  name: v.pipe(v.string(), v.minLength(1)),
})

type User = v.InferOutput<typeof User>

const parsed = v.parse(User, { email: 'foo@example.com', age: 30, name: 'Foo' })

v.pipe(...)でバリデータを合成し、v.parse/v.safeParseで適用。関数はすべて名前付きエクスポート — 未使用はesbuild・Rollupが落とす。

バンドル差

公式サイトとコミュニティのベンチマークが示す絵。

  • v.string()を一度だけ使うと、Valibotで入るコードは数百バイトレベル。
  • 同じコードがZod 3では数十KB。Zod 4で大幅に縮小したが依然Valibotより大きい。
  • 大きなスキーマ(多数のバリデータ)を全部使うと差は縮む。小さく始めるほどValibotが有利。

ValibotのBaseSchema API

Valibot 1.0 GA(2024)以降API安定。主要関数。

  • v.object, v.array, v.tuple, v.union, v.intersect, v.record
  • v.string, v.number, v.boolean, v.literal, v.date, v.bigint
  • v.pipe(schema, ...actions) — バリデータ合成
  • v.transform(fn), v.brand(name) — 変換・ブランド型

非同期検証は別途v.parseAsync/v.safeParseAsync

Valibotの強み

  • バンドルサイズ圧倒的 — フロントエンド・エッジでどこよりも小さい。
  • 型推論品質InferOutputInferInput両方が正確。
  • 関数型だから合成が容易 — 新しいバリデータを作るコストが低い。
  • 急成長中のエコシステム — Standard Schema対応、@valibot/to-json-schema@hookform/resolvers/valibot

Valibotの弱み

  • API学習コスト — チェーンに慣れた人には最初ぎこちない。
  • エラーメッセージのカスタマイズはやや手間
  • Zodアダプタを提供する一部ライブラリでValibotアダプタが未対応または遅れて追加
  • OpenAPIエクスポートでZod並みに厚いパッケージがない — 手書きが増える。

5章 · ArkType — TypeScript構文をそのままスキーマに

ArkTypeは2024年にv2をGAリリースして注目を集めた。発想が違う。

「TypeScript表現を文字列として受け取り、そのままランタイムスキーマにする。」

同じUserスキーマ。

import { type } from 'arktype'

const User = type({
  email: 'string.email',
  age: 'number.integer >= 0',
  name: 'string > 0',
})

// type推論: TSが表現を正確に理解
type User = typeof User.infer

const out = User({ email: 'foo@example.com', age: 30, name: 'Foo' })
if (out instanceof type.errors) {
  console.error(out.summary)
} else {
  // out is the validated value
}

肝は'number.integer >= 0'のような表現がTSコンパイラによって正確な型に推論されること。ArkTypeは自前のミニDSLを作り、TSテンプレートリテラル型でパースする。結果として手書きのTS型とスキーマが100%一致する。

ArkTypeの差別点

  • TS表現そのまま'string | number''(string | number)[]>=2'等が馴染みやすい。
  • 型ファーストtypeof User.inferが即時に使える。追加の.infer呼び出しコストなし(型レベル)。
  • JSON Schemaエクスポート組み込みUser.toJsonSchema()一行。
  • ランタイム性能 — コンパイル済みバリデータ — スキーマ定義時に内部で検証関数を予めビルド。

ArkType オブジェクト定義の別形

文字列だけでなくTSオブジェクトをそのまま渡せる。

import { type } from 'arktype'

const Post = type({
  id: 'number.integer',
  title: 'string > 0',
  tags: 'string[]',
  author: {
    id: 'number',
    name: 'string',
  },
  publishedAt: 'Date',
})

ArkTypeの強み

  • TS親和性が最高 — TSをよく知っている人にとって学習コストはほぼゼロ。
  • JSON Schema組み込み — 外部パッケージ不要。
  • ランタイム高速 — コンパイル済みバリデータ。
  • 関数呼び出しがスキーマ = User(value) — APIが直感的。

ArkTypeの弱み

  • エコシステムがまだ小さい — tRPC・React Hook FormアダプタはあるがZodほど厚くない。
  • エラーメッセージの日本語化・カスタマイズ資料が少ない
  • 非同期検証は部分対応 — 同期中心の設計。
  • DSL文字列に慣れが必要'number.integer >= 0'は最初は魔法のように感じる。

6章 · TypeBox — 書いたものがそのままJSON Schema

TypeBoxは2021年登場後、Fastify陣営で愛されてきたが、AIツール呼び出し時代に再注目された。

「私が書いたスキーマがそのままJSON Schema。AJVでコンパイルすれば最速ランタイム。」

同じUserスキーマ。

import { Type, Static } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'

const User = Type.Object({
  email: Type.String({ format: 'email' }),
  age: Type.Integer({ minimum: 0, maximum: 150 }),
  name: Type.String({ minLength: 1 }),
})

type User = Static<typeof User>

const ok = Value.Check(User, unknown)
const errors = [...Value.Errors(User, unknown)]

Type.Object({ ... })が返す値はJSON Schemaそのもの。JSON.stringify(User)すれば標準JSON Schemaドキュメントになる。

AJV結合 — 最速のランタイム

import Ajv from 'ajv'
import addFormats from 'ajv-formats'
const ajv = addFormats(new Ajv())

const check = ajv.compile(User)
const ok = check(value) // コンパイル済み関数を直接呼ぶ — 非常に速い

AJVはJSON Schemaを受け取り、V8親和的なコードで検証関数をビルドする。結果は一般にこのカテゴリで最速のランタイム検証

TypeBox + Fastify

import Fastify from 'fastify'

const app = Fastify().withTypeProvider<TypeBoxTypeProvider>()

app.post('/users', {
  schema: { body: User, response: { 200: User } },
}, async (req) => {
  // req.body is typed as User automatically
  return req.body
})

スキーマがそのままOpenAPI応答仕様 + ランタイム検証 + 応答シリアライザ。1定義4仕事。

AIツール呼び出しでのTypeBox

OpenAI・Anthropic・Geminiの関数/ツール定義はJSON Schema

const GetWeather = Type.Object({
  city: Type.String(),
  units: Type.Optional(Type.Union([Type.Literal('c'), Type.Literal('f')])),
})

const tool = {
  type: 'function',
  function: {
    name: 'get_weather',
    description: '指定された都市の現在の天気',
    parameters: GetWeather, // already JSON Schema
  },
}

zod-to-json-schema等の変換器を経ずに直接使える。変換で失う情報(formatdescription等)がない。

TypeBoxの強み

  • JSON Schema標準そのまま — OpenAPI・AIツール呼び出し・AJVすべてに直結。
  • AJVで最速ランタイム — ホットパス検証に最適。
  • Fastify・Hono・Expressミドルウェア豊富
  • バンドル非常に小さいType.*自体が軽い。

TypeBoxの弱み

  • APIがやや冗長Type.Object({ x: Type.String() })が長く感じる。
  • チェーンの魅力はないz.string().email().min(5)の短さが恋しくなる。
  • エコシステムがFastify寄り — Reactフォームライブラリのアダプタは少ない。
  • 非同期検証は弱い — JSON Schema自体が同期中心。

7章 · Effect Schema — Effectエコシステムの検証ツール

EffectはScala・Haskellの影響を受けたフルスタックTS関数型フレームワーク。Effect Schemaはその中のデータ検証・変換モジュール。

「検証は双方向変換である。そしてすべてのエラーは型に入る。」

同じUserスキーマ。

import { Schema } from 'effect'

const User = Schema.Struct({
  email: Schema.String.pipe(Schema.email()),
  age: Schema.Number.pipe(Schema.int(), Schema.between(0, 150)),
  name: Schema.NonEmptyString,
})

type User = Schema.Schema.Type<typeof User>

const decode = Schema.decodeUnknownSync(User)
const out = decode({ email: 'foo@example.com', age: 30, name: 'Foo' })

decodeUnknownSyncは同期検証。decodeUnknownまたはdecodeはEffect値を返す — Effectフルスタックの中で合成可能。

Effect Schemaの差別点

  • 双方向 — decodeencode — JSON → ドメイン、ドメイン → JSON両方。Date・BigDecimal・Brand等のドメイン型を自然に扱える。
  • 型付きエラーParseErrorが型に入っているので、エラー処理漏れをコンパイラが捕える。
  • Schema合成Schema.composeSchema.transformSchema.filterで合成。
  • JSON Schema組み込みJSONSchema.make(User)一行。

双方向変換の例

import { Schema } from 'effect'

// JSON stringで来るDateをドメインDateに
const DateFromString = Schema.transform(Schema.String, Schema.Date, {
  decode: (s) => new Date(s),
  encode: (d) => d.toISOString(),
})

const Event = Schema.Struct({
  name: Schema.NonEmptyString,
  at: DateFromString,
})

const decoded = Schema.decodeUnknownSync(Event)({ name: 'Launch', at: '2026-05-14T00:00:00Z' })
//      ^ decoded.at is real Date

Effect Schemaの強み

  • 双方向検証・変換の優雅さ — 他のライブラリで別途書く必要がある仕事が自然に解ける。
  • 型付きエラーでエラー処理漏れを遮断
  • Effectフルスタックと合成 — HTTP・DB・キャッシュ・リトライまで1モデル。
  • JSON Schema組み込み

Effect Schemaの弱み

  • Effectを使わないなら学習・バンドルコストが大きい — Effectコアが一緒に入る。
  • チェーンに比べてコードが長く見える.pipe(Schema.email())に慣れるまでぎこちない。
  • エコシステム — フォーム・tRPCアダプタは部分的
  • 学習曲線 — Effect本体の学習が必要。圏論・関数型親和が要る。

8章 · 同じスキーマ、5ライブラリ — User(email + age + name)

5つを横並びで見る。同じ意味のスキーマ。

8.1 Zod

import { z } from 'zod'

const User = z.object({
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  name: z.string().min(1),
})

type User = z.infer<typeof User>

8.2 Valibot

import * as v from 'valibot'

const User = v.object({
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150)),
  name: v.pipe(v.string(), v.minLength(1)),
})

type User = v.InferOutput<typeof User>

8.3 ArkType

import { type } from 'arktype'

const User = type({
  email: 'string.email',
  age: '0 <= number.integer <= 150',
  name: 'string > 0',
})

type User = typeof User.infer

8.4 TypeBox

import { Type, Static } from '@sinclair/typebox'

const User = Type.Object({
  email: Type.String({ format: 'email' }),
  age: Type.Integer({ minimum: 0, maximum: 150 }),
  name: Type.String({ minLength: 1 }),
})

type User = Static<typeof User>

8.5 Effect Schema

import { Schema } from 'effect'

const User = Schema.Struct({
  email: Schema.String.pipe(Schema.email()),
  age: Schema.Number.pipe(Schema.int(), Schema.between(0, 150)),
  name: Schema.NonEmptyString,
})

type User = Schema.Schema.Type<typeof User>

比較 — 同じスキーマ、違う哲学

ライブラリ行数文法スタイル推論呼び出し
Zod短いチェーンz.infer
Valibot関数合成(pipe)v.InferOutput
ArkType最短TS DSL文字列typeof X.infer
TypeBoxType ビルダーStatic
Effect Schema.pipeチェーンSchema.Schema.Type

読みやすさはZod・ArkTypeが強く、バンドル・ツリーシェイキングはValibot・TypeBoxが強く、変換・エラー型まで担えばEffect Schemaが強い。


9章 · AIツール呼び出し時代 — JSON Schemaが強制される

2024〜2025の最大の流れ。すべての主要LLMがツール定義をJSON Schemaで受ける

9.1 OpenAI function calling

const tools = [{
  type: 'function',
  function: {
    name: 'get_weather',
    description: 'Get the current weather in a given city',
    parameters: {
      type: 'object',
      properties: {
        city: { type: 'string' },
        units: { type: 'string', enum: ['c', 'f'] },
      },
      required: ['city'],
    },
  },
}]

parametersは標準JSON Schema。オブジェクトを直接書くか、TSスキーマライブラリから変換する。

9.2 Anthropic tool use

const tools = [{
  name: 'get_weather',
  description: 'Get the current weather in a given city',
  input_schema: {
    type: 'object',
    properties: {
      city: { type: 'string' },
      units: { type: 'string', enum: ['c', 'f'] },
    },
    required: ['city'],
  },
}]

キー名が違うだけ(input_schema)。形は同じ。

9.3 ライブラリ別JSON Schema変換

  • Zod@asteasolutions/zod-to-openapizod-to-json-schema、OpenAI SDKがzodFunctionヘルパで直接受けることも。Anthropic SDKもZod直接対応が進む。
  • Valibot@valibot/to-json-schema公式パッケージ。
  • ArkTypeUser.toJsonSchema()組み込み。
  • TypeBox — 変換不要。定義自体がJSON Schema。
  • Effect SchemaJSONSchema.make(User)組み込み。

9.4 変換時に失うもの

JSON SchemaはTS表現より限定的。変換時にしばしば失われるもの。

  • z.transform/v.transform等の双方向変換 — JSON Schemaは入力検証のみ。
  • z.brandで作ったブランド型 — 単純なstring等に平坦化される。
  • 細かいrefine関数 — JSON Schemaに表現できない検証は消える。
  • 再帰スキーマ$refの扱いはライブラリ・LLM側がうまく受けないことが多い。平坦化や深さ制限が安全。

9.5 OpenAI structured outputsとstrictモード

OpenAIが2024年後半に出したstructured outputsはJSON Schemaの一部制約(例: additionalProperties: false、必須フィールド、$ref非対応等)を強制する。TSライブラリの吐くJSON Schemaがその制約を破るとOpenAI側が拒否する。

実務のヒント。

  • ツール呼び出し用スキーマは可能な限りシンプルに(平坦、additionalProperties: false)。
  • descriptionフィールドを積極活用 — LLMが意味を理解する最良の手掛かり。
  • 再帰・$refは避けるかLLM親和的に平坦化。
  • 変換結果を一度sanity check — 変換後のJSON Schemaを直接見て意図通りか確認。

10章 · 7軸の詳細比較

10.1 バンドルサイズ

純粋なコアで小さなスキーマ一つを使う前提。

ライブラリ小さい用途大きい用途備考
Valibot最小(数百B)ツリーシェイキングの頂点
TypeBox非常に小さいType.*自体が軽い
ArkTypeパーサーも一緒に入る
Zod 4中(大幅減)v4-miniはさらに小さい
Zod 3ツリーシェイキング弱い
Effect SchemaEffectコア含む
Yup
Joiブラウザ親和性低い

10.2 TypeScript推論品質

ライブラリ推論品質備考
ArkType最高TS表現そのまま
Zod 4非常に良いz.infer広範
Valibot非常に良いInferOutput/InferInput
Effect Schema非常に良い双方向型まで
TypeBox良いStatic
Yup普通推論は弱い
Joi弱い別途型作成必要

10.3 JSON Schemaエクスポート

ライブラリ対応方法
TypeBoxネイティブ定義自体
ArkType組み込み.toJsonSchema()
Effect Schema組み込みJSONSchema.make()
Valibot公式外部@valibot/to-json-schema
Zod外部zod-to-json-schema@asteasolutions/zod-to-openapi
Yup貧弱非公式変換器
Joiなし手書き

10.4 非同期検証

ライブラリ対応パターン
Zod強いz.string().refine(async ...) + parseAsync
Valibot強いparseAsync/safeParseAsync
Effect Schema強いEffect自体が非同期
ArkType部分同期中心
TypeBox弱い同期検証中心

10.5 OpenAPIエクスポート

ライブラリ対応ツール
TypeBox非常に良いFastify + Type Provider
ArkType良い.toJsonSchema()直結
Zod良い@asteasolutions/zod-to-openapi@hono/zod-openapi
Effect Schema良いEffect HTTP·OpenAPIアダプタ
Valibot良い@valibot/to-json-schema

10.6 ランタイムコスト(ホットパス前提のおおよそ)

ライブラリ検証速度備考
TypeBox + AJV(コンパイル)最速V8親和コンパイル
Valibot非常に速い関数合成が単純
ArkType速いコンパイル済みバリデータ
Zod 4速い大幅改善
Effect Schema中〜速Effectオーバーヘッド
Zod 3普通深いオブジェクトに弱い
Joi遅い重い古典API

(正確な数値はケース次第。各ライブラリ公式ベンチマークと自分のワークロードで計測推奨。)

10.7 エコシステム(アダプタ・統合)

ライブラリtRPCRHFOpenAI/Anthropic SDKFastifyHonoNext.js
Zodファーストファーストファーストファースト
Valibot対応ファースト(@hookform/resolvers/valibot)外部変換
ArkType対応対応外部変換
TypeBox部分部分直接ファーストファースト(@hono/typebox-validator)
Effect Schema部分部分外部変換

11章 · Yup・Joi・Superstruct — 生きているが

11.1 Yup

Formik時代の標準。フォーム検証では今も使われるが新規採用は減る。Zod・Valibotに比べてTS推論が弱くバンドルが重い。

import * as yup from 'yup'

const User = yup.object({
  email: yup.string().email().required(),
  age: yup.number().integer().min(0).max(150).required(),
  name: yup.string().min(1).required(),
})

11.2 Joi

Nodeバックエンドの古典標準。ブラウザ親和性が低い(バンドル大、依存が深い)。サーバー検証のレガシーコードベースに多く残っている。

const Joi = require('joi')
const User = Joi.object({
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(0).max(150).required(),
  name: Joi.string().min(1).required(),
})

11.3 Superstruct

小さく軽量な検証。io-tsより親しみやすいが、Zod・Valibotに比べてエコシステムは小さい。小規模プロジェクト・ライブラリ内部検証には依然良い選択。

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

const Email = refine(string(), 'email', (v) => /.+@.+/.test(v))
const User = object({
  email: Email,
  age: number(),
  name: string(),
})

11.4 io-ts

伝統的関数型陣営。Effect Schemaがその座を急速に奪っている。新規コードでio-tsを自発的に始める例は減る。


12章 · Standard Schema — ライブラリ間の互換

2024年末〜2025年に登場したStandard Schema仕様は、検証ライブラリ群が合意した最小インターフェース。tRPC・Hono・React Hook Form等のツールが一つのアダプタでZod・Valibot・ArkTypeを受けられるようにする試み。

コアインターフェース(簡略化)。

interface StandardSchemaV1<Input = unknown, Output = Input> {
  '~standard': {
    version: 1
    vendor: string
    validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>
    types?: { input: Input; output: Output }
  }
}

Zod・Valibot・ArkType・Effect Schemaすべてがこのインターフェースを実装。結果 — ライブラリ選択のコストがさらに下がった。1コードベースに複数ライブラリが共存する、別ツールへ乗り換える際のコストが減った。

ただしStandard Schemaは最小共通でしかなく、ライブラリ固有機能(transform、refine、brand等)はアダプタ作者の仕事として残る。


13章 · 何をいつ選ぶか — 正直な結論

正解はいつもの通り**「状況による」**。ただパターンはある。

Zod 4を選ぶとき

  • 最大のエコシステムが必要。tRPC・Next.js・React Hook Form・OpenAI/Anthropic SDKすべてがファースト。
  • 学習資料・チュートリアル・回答プール(Stack Overflow)が最も厚い必要。
  • バンドル1KBの差が問題にならないバックエンド・フルスタック。
  • チームにZod経験者が既にいる。

Valibotを選ぶとき

  • フロントエンド・エッジ優先。バンドルがそのままコスト。
  • 関数型合成を好む。
  • React Hook Form・React Router・Next.jsクライアントフォーム検証。
  • 「Zodとほぼ同じ仕事をより小さく」 — 明確な動機。

ArkTypeを選ぶとき

  • TS推論品質が最高でないと困る。
  • TS表現をそのままスキーマにする発想が自然。
  • JSON Schema組み込みが必要(外部パッケージ依存回避)。
  • 自前のミニDSLに適応できるチーム。

TypeBoxを選ぶとき

  • APIバックエンド — Fastify・Hono・Express + OpenAPI。
  • AIツール呼び出し — JSON SchemaをLLMに直送。
  • ホットパス検証 — AJVで最速ランタイム。
  • スキーマ = OpenAPI = 検証 = シリアライザ。1定義4仕事。

Effect Schemaを選ぶとき

  • チームが既にEffectフルスタックを行く途中。
  • 双方向エンコーディング・変換がコア(Date・Brand・BigDecimal等)。
  • 型付きエラーをコンパイラに強制させたい。
  • 関数型・圏論親和的なチーム。

Yup・Joi・Superstructを選ぶとき

  • レガシーコードベースの保守。
  • Yup — Formik基盤のコード。
  • Joi — Hapi.jsバックエンド。
  • Superstruct — 依存最小の小規模ライブラリ。

アンチチェックリスト(避けるべきパターン)

  • 1コードベースに検証ライブラリ3つ — Standard Schemaがあっても学習・デバッグコストは累積。
  • 「ValibotがZodより小さいから乗り換えた」決定を計測なしで — 大きなスキーマでは差が縮む。
  • TypeBoxですべてのフォーム検証 — フォームライブラリのアダプタが貧弱。Zod・Valibotが自然。
  • AIツール呼び出しにZodをそのまま投げる — 変換を経由しつつ、変換後のJSON Schemaを一度点検。
  • z.refine/v.checkに変換できない検証を多用 — 変換後にその検証は消える。コア検証は変換可能な形に。

エピローグ — スキーマはAPI境界の真実

検証ライブラリの選択はどれだけ小さいか、どれだけ馴染みやすいか、どれだけ遠くまで連れて行けるかのトレードオフ。一方が優越するわけではない。ただ2026年5月の風景は明確だ。

  • JSON Schema互換が1軍資格になった — AIツール呼び出しが強制した。Zodも外部パッケージで追いつき、TypeBox・ArkType・Effect Schemaは組み込み。
  • バンドル競争はまだ終わっていない — Valibotが頂点を示し、Zod 4が大幅に追いついた。差は小さくなるが依然意味のある差。
  • TS推論は標準資格 — どんなツールも推論が弱ければ新規採用で落ちる。
  • Standard Schemaで乗り換えコストが下がった — もはや「一度決めたら一生」ではない。
  • フルスタック1スキーマが次第に普遍 — 同じスキーマでサーバー検証・クライアントフォーム・ドキュメント・AIツール呼び出しまで。

意思決定チェックリスト

  • API境界が最優先か? — TypeBoxまたはZod + zod-to-openapi
  • フォーム・バンドル・エッジが最優先か? — ValibotまたはZod 4-mini。
  • TS推論品質が最高でないと困るか? — ArkType。
  • AIツール呼び出しJSON Schemaが中心か? — TypeBoxまたはArkType。
  • Effectフルスタック中か? — Effect Schema。
  • 学習・チュートリアル・エコシステムの厚さが重要? — Zod。
  • 非同期検証が日常? — Zod・Valibot・Effect Schema。
  • 双方向変換・ブランド型が必要? — Effect SchemaまたはZod transform。

アンチパターン

  1. 計測なしでライブラリ乗り換え — 決定は計測後。
  2. AIツールに変換結果を見ずに投げる — additionalPropertiesrequired漏れが頻発。
  3. JSON Schemaに表現できないrefineをコア検証にする — 変換時に消える。
  4. フォーム検証にTypeBox — RHFアダプタが弱い。Zod・Valibotが自然。
  5. 大きなオブジェクトに毎リクエストz.parseを繰り返す — ホットパスならコンパイル済みバリデータ(TypeBox + AJV)を検討。
  6. 1スキーマ2つの真実の源泉 — DBスキーマと検証スキーマが別の場所に定義されてずれる。
  7. エラーメッセージのカスタマイズを怠る — ライブラリデフォルトメッセージがユーザーに露出。

次回予告

候補: AIツール呼び出しJSON Schemaのデバッグ — OpenAI structured outputs・Anthropic tool useでよくあるミス集Zod 4移行レポート — 5万行コードベース1ヶ月の作業Valibot深掘り — ツリーシェイカーが実際に何を落とすか

"検証はコードの境界である。境界が明確なコードだけが遠くまで行ける。"

— TypeScriptデータ検証 2026、終わり。


参考 / References