Most of these libraries exist to give you a schema and a TypeScript type from the same declaration. This page compares the types themselves: whether they describe your data, what they cost the compiler, and how they read.
Every library declares the same product schema, and its type is read with whatever the library provides:
Copy to clipboardconst schema = z.object({ id: z.number(), title: z.string(), tags: z.array(z.string()), }); type Output = z.output<typeof schema>; type Input = z.input<typeof schema>;
Copy to clipboardconst schema = z.object({ id: z.number(), title: z.string(), tags: z.array(z.string()) }); type Output = z.output<typeof schema>; type Input = z.input<typeof schema>;
Input and Output pass only when the inferred type and the data type describe each other
exactly. A type that allows more (unknown, or any, which checks nothing) leaves the work to
runtime; one that allows less rejects values the schema accepts.
From type is the other direction: building a schema for a type you already have. It is checked only when the compiler rejects every way the two can disagree - a field of the wrong type, a field left out, a field required where the type makes it optional, and a field the type never declared. Unchecked means some of those compile, and the schema they build disagrees with the type it was written for.
Instantiations is the work the compiler does to declare the schema and read its output type. It is what grows into slow editor feedback and slow builds as a codebase adds schemas.
Type on hover is the type an editor shows for the schema, and its length in characters. The same text appears in every error message that mentions it.
Open a row to read the types in full.
Measured with TypeScript 6.0.3.
| Library | Version | /wk | Input | Output | From type | Instantiations | Type on hover | |||
|---|---|---|---|---|---|---|---|---|---|---|
zod | 4.6.5 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | BuilderRejects every way a schema can disagree with the type. | 1_185 | 660 | Open details | ||
superstruct | 2.0.2 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationRejects every way a schema can disagree with the type. | 1_254 | 2.06K | Open details | ||
effect@rc | 4.0.0-rc.112 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 1_632 | 885 | Open details | ||
zod/mini | 4.6.5 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | BuilderRejects every way a schema can disagree with the type. | 1_640 | 958 | Open details | ||
io-ts | 2.2.22 | n/a | WiderThe inferred type allows values the data type doesn't, such as unknown. | ExactThe inferred type and the data type describe each other. | AnnotationRejects every way a schema can disagree with the type. | 1_697 | 966 | Open details | ||
decoders | 2.10.1 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 1_839 | 446 | Open details | ||
@railway-ts/pipelines | 0.1.29 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 2_133 | 457 | Open details | ||
@remix-run/data-schema | 0.3.0 | n/a | WiderThe inferred type allows values the data type doesn't, such as unknown. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 2_462 | 454 | Open details | ||
yup | 1.7.1 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 2_765 | 732 | Open details | ||
typebox | 1.3.33 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 4_070 | 597 | Open details | ||
runtypes | 7.0.5 | n/a | AnyThe inferred type contains any, so that part of it is not type checked. | AnyThe inferred type contains any, so that part of it is not type checked. | AnnotationAccepts a schema with:
| 4_995 | 868 | Open details | ||
valibot | 1.5.0 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 8_539 | 2.73K | Open details | ||
sury | 11.0.0 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | BuilderRejects every way a schema can disagree with the type. | 10_619 | 884 | Open details | ||
effect | 3.22.2 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationRejects every way a schema can disagree with the type. | 10_997 | 954 | Open details | ||
@sapphire/shapeshift | 5.0.0 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 12_197 | 744 | Open details | ||
arktype | 2.2.3 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 28_869 | 447 | Open details | ||
zod/v3 | 4.6.5 | n/a | ExactThe inferred type and the data type describe each other. | ExactThe inferred type and the data type describe each other. | AnnotationAccepts a schema with:
| 29_496 | 2.31K | Open details | ||
@paseri/paseri | 1.9.7 | 167 | AnnotationAccepts a schema with:
| - | - | Open details | ||||
typia | 14.0.6 | n/a | Builder, generated from the typeRejects every way a schema can disagree with the type. | - | - | Open details | ||||