LabHub

ブログ

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

한국어English日本語

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

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

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

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

この記事は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章で短く扱う。

なぜこの五つか


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は二つの批判を長く受けた。

Zod 4が変えたもの

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

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)

Zodの弱み(2026)


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が落とす。

バンドル差

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

ValibotのBaseSchema API

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

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

Valibotの強み

Valibotの弱み


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の差別点

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の強み

ArkTypeの弱み


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の強み

TypeBoxの弱み


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の差別点

双方向変換の例

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 Schemaの弱み


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変換

9.4 変換時に失うもの

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

9.5 OpenAI structured outputsとstrictモード

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

実務のヒント。


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を選ぶとき

Valibotを選ぶとき

ArkTypeを選ぶとき

TypeBoxを選ぶとき

Effect Schemaを選ぶとき

Yup・Joi・Superstructを選ぶとき

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


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

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

意思決定チェックリスト

アンチパターン

  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

コメント

まだコメントはありません。

ログインするとコメントできます