Skip to content
A serene ark, sailing to runtime

ArkType

TypeScript's 1:1 validator, optimized from editor to runtime

What awaits

Unparalleled DX

Type syntax you already know with safety and completions unlike anything you’ve ever seen

const 
const user: Type<{
    name: string;
    platform: "android" | "ios";
    version?: number | string;
}>
user
= type({
name: "string", platform: "'android' | 'ios'", "version?": "number | s
  • number | string
  • number | symbol
  • number | semver
"
})

Faster... everything

100x faster than Zod at runtime with editor performance that will remind you how autocomplete is supposed to feel

Object Validation, Node v22.2.0 (source)
ArkType (15 ns)
  Zod (1374 ns)

Clarity and Concision

Definitions are half as long, type errors are twice as readable, and hovers tell you just what really matters

// hover me
const 
const user: Type<{
    name: string;
    platform: "android" | "ios";
    versions?: (number | string)[];
}>
user
= type({
name: "string", platform: "'android' | 'ios'", "versions?": "number | string)[]"
TypeScript: Unmatched ) before [] 
})

Better Errors

Deeply customizable messages with great defaults

const 
const out: {
    name: string;
    platform: "android" | "ios";
    versions?: (string | number)[];
} | ArkErrors
out
= user({
name: "Alan Turing", platform: "enigma", versions: [0, "1", 0n] }) if (
const out: {
    name: string;
    platform: "android" | "ios";
    versions?: (string | number)[];
} | ArkErrors
out
instanceof type.errors) {
// hover summary to see validation errors console.error(const out: RuntimeErrorsout.RuntimeErrors.summary: string
platform must be "android" or "ios" (was "enigma") versions[2] must be a number or a string (was bigint)
summary
)
}

Deep Introspectability

ArkType uses set theory to understand and expose the relationships between your types at runtime the way TypeScript does at compile time

const user: Type<{
    name: string;
    device: {
        platform: "android" | "ios";
        version?: string | number;
    };
}>
user
.extends("object") // true
const user: Type<{
    name: string;
    device: {
        platform: "android" | "ios";
        version?: string | number;
    };
}>
user
.extends("string") // false
// true (string is narrower than unknown)
const user: Type<{
    name: string;
    device: {
        platform: "android" | "ios";
        version?: string | number;
    };
}>
user
.extends({
name: "unknown" }) // false (string is wider than "Alan")
const user: Type<{
    name: string;
    device: {
        platform: "android" | "ios";
        version?: string | number;
    };
}>
user
.extends({
name: "'Alan'" })

Intrinsic Optimization

Every schema is internally normalized and reduced to its purest and fastest representation

// all unions are optimally discriminated
// even if multiple/nested paths are needed
const 
const account: Type<{
    kind: "admin";
    powers?: string[];
} | {
    kind: "superadmin";
    superpowers?: string[];
} | {
    kind: "pleb";
}>
account
= type({
kind: "'admin'", "powers?": "string[]" }).or({ kind: "'superadmin'", "superpowers?": "string[]" }).or({ kind: "'pleb'" })