JSON Schema vs TypeScript: Runtime vs Compile-Time Validation
Last updated:
TypeScript and JSON Schema both describe data shapes, but at different layers: TypeScript catches type errors at compile time (before the program runs); JSON Schema validates data at runtime (when external data arrives). TypeScript has 0 runtime cost — types are erased after compilation. JSON Schema validation with Ajv adds ~0.5–2 ms per request for typical schemas. The 3 approaches for using both together: (1) write TypeScript interfaces and a matching JSON Schema separately, (2) use Zod to define a schema that generates both a TypeScript type and a runtime validator, (3) use json-schema-to-typescript to generate interfaces from existing JSON Schema files. This guide covers all 3 patterns.
The core difference
| TypeScript | JSON Schema | |
|---|---|---|
| When it runs | Compile time (build step) | Runtime (your code calls a validator) |
| What it validates | Your code's type usage | External data (API bodies, user input) |
| Output | Build error or warning | Pass/fail + error messages |
| Language | TypeScript only | Any language |
| Validates external input | No — types are erased at runtime | Yes |
| Portable | No — TypeScript-specific | Yes — JSON, language-agnostic |
Why TypeScript alone is not enough for external data
TypeScript types are erased when the code compiles to JavaScript. At runtime, if an API returns an unexpected shape, TypeScript cannot catch it.
// TypeScript thinks this is safe at compile time
const res = await fetch('/api/user')
const user: User = await res.json() // No runtime check!
console.log(user.name.toUpperCase()) // Could throw if name is nullTypeScript gives you confidence about code you write. It cannot give you confidence about data that arrives at runtime from an external source.
What JSON Schema adds
JSON Schema validates the actual shape of a value at runtime. If an API response does not match the schema, validation fails with a descriptive error message before your code tries to use the data.
const userSchema = {
type: 'object',
required: ['id', 'name', 'email'],
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string', format: 'email' },
},
additionalProperties: false,
}
// Validate before using
const valid = ajv.validate(userSchema, data)
if (!valid) throw new Error(ajv.errorsText())For schema examples across common patterns, see JSON Schema Examples.
Using both together
The most reliable pattern is to define a JSON Schema for external data, validate at the boundary (API handler, form submit, file read), and then assert a TypeScript type after successful validation.
import Ajv from 'ajv'
const ajv = new Ajv()
interface User {
id: number
name: string
email: string
}
const userSchema = { /* ... */ }
const validateUser = ajv.compile<User>(userSchema)
function parseUser(raw: unknown): User {
if (!validateUser(raw)) {
throw new Error(ajv.errorsText(validateUser.errors))
}
return raw // TypeScript now knows this is User
}Libraries like zod and valibot unify both steps: you define a schema once and get both runtime validation and inferred TypeScript types.
When to reach for each
| Scenario | Use |
|---|---|
| Internal function signatures | TypeScript |
| Validating an API response | JSON Schema or zod |
| Validating user-submitted form data | JSON Schema or zod |
| Documenting an API contract (OpenAPI) | JSON Schema |
| Cross-language validation (Python, Go, Java) | JSON Schema |
| Database record types | TypeScript + ORM types |
Generating TypeScript types from JSON Schema
If you already have a JSON Schema, tools like json-schema-to-typescript can generate TypeScript interfaces from it, keeping your types and schema in sync.
Going the other direction — inferring a schema from a sample JSON payload — is useful when you have real data but no schema yet. See the JSON to TypeScript tool for instant interface generation from a sample object.
Validate a JSON Schema online
Test your schema against real data in Jsonic's JSON Schema Validator. Paste a schema and a payload and see validation errors highlighted immediately.
Open Schema ValidatorFrequently asked questions
What is the main difference between JSON Schema and TypeScript types?
TypeScript validates at compile time — types are erased when code runs. JSON Schema validates at runtime — when your code receives actual data from an API, file, or user input. TypeScript cannot catch runtime data shape errors from external sources.
When should I use JSON Schema instead of TypeScript?
Use JSON Schema for API request/response validation, form data, configuration files, and webhook payloads. Also use it when you need language-agnostic validation across Python, Go, Java, and other languages. TypeScript alone cannot protect against bad runtime data.
Can I generate TypeScript from JSON Schema?
Yes. The "json-schema-to-typescript" npm package generates TypeScript interfaces from a JSON Schema file, keeping types and schema in sync. "quicktype" also supports this direction. This approach works well in OpenAPI-first development workflows.
Can I generate JSON Schema from TypeScript?
Yes. "typescript-json-schema" and "ts-json-schema-generator" generate JSON Schema from TypeScript interfaces. Alternatively, Zod and Valibot let you define a schema once and automatically infer the TypeScript type, eliminating the need for separate generation.
What is the difference between compile-time and runtime validation?
Compile-time validation catches type errors in your code before it runs. Runtime validation checks the actual values your code processes during execution. TypeScript is compile-time only; it cannot validate data arriving from external APIs at runtime.
Which tools convert between JSON Schema and TypeScript?
Schema to TypeScript: "json-schema-to-typescript", "quicktype". TypeScript to schema: "typescript-json-schema", "ts-json-schema-generator". Unified approach (no conversion needed): Zod, Valibot, and Typia define schema and type together.
Recommended reading
- Effective TypeScript (2nd Edition) — Dan Vanderkam83 concrete ways to level up your TypeScript — inference, narrowing, and type-safe API boundaries.
- Designing Data-Intensive Applications (2nd Edition) — Martin Kleppmann & Chris RiccominiThe modern classic on data systems — encoding formats, schemas, replication, and stream processing.
As an Amazon Associate, Jsonic earns from qualifying purchases.