Skip to content
Development

JSON Schema vs TypeScript Types: Where Each Protects Your Data

Static types help you use a value correctly; runtime validation checks whether the value deserves those assumptions.

JSON Schema vs TypeScript Types: Where Each Protects Your Data

Key takeaways

  • TypeScript annotations and assertions do not validate incoming data at runtime.
  • JSON Schema describes constraints that a compatible validator can apply to actual JSON values.
  • Required fields, extra properties, numeric constraints, and format handling need explicit decisions.
  • Derive types and validation from an authoritative contract where practical, while checking conversion limits.

JSON Schema and TypeScript types describe data, but they operate at different points in an application.

TypeScript checks code during development and compilation. JSON Schema provides a language-independent description that a validator can use to check actual values at runtime.

Imagine an order endpoint expecting a product identifier and quantity. The frontend is written in TypeScript, but requests can also come from an old mobile client, a partner integration, or a direct HTTP call. The server cannot assume those callers obey its types.

A Type Is Useful Inside the Program

The application might define its request like this:

type OrderRequest = {
  productId: number;
  quantity: number;
};

TypeScript can flag a string assigned to quantity in checked code. It also gives developers useful completion and refactoring support.

However, this assertion does not inspect a response:

const order = JSON.parse(rawBody) as OrderRequest;

The input could contain "quantity": "many", and the assertion would still perform no validation. The TypeScript handbook explains that assertions are removed during compilation.

Treat incoming data as unknown until a runtime check establishes the properties the application needs.

A Schema Makes the Boundary Rules Explicit

A JSON Schema for this endpoint could be:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "productId": { "type": "integer", "minimum": 1 },
    "quantity": { "type": "integer", "minimum": 1, "maximum": 100 }
  },
  "required": ["productId", "quantity"],
  "additionalProperties": false
}

A compatible validator can reject strings, fractional quantities, missing fields, and quantities outside the permitted range.

The schema document does not enforce itself. The server must actually invoke validation before passing the value into order-processing code.

JSON Schema’s object documentation distinguishes property definitions from required fields and explains how additional properties are handled.

Similar Shapes Can Have Different Meanings

The TypeScript declaration and schema above are related, but they do not express identical guarantees.

ConcernTypeScript declaration aboveJSON Schema above
Quantity is numericExpressed as numberExpressed as integer
Quantity is between 1 and 100Not expressedEnforced during validation
Both properties existRequired by the typeListed in required
No other properties existNot a universal exact-object guaranteeRejected by additionalProperties: false

An omitted property is also different from one whose value is null. If null is allowed, model it deliberately.

These differences matter when generating one representation from another. The conversion may preserve the structural shape while losing a runtime restriction.

Parsing, Validation, and Authorization Are Separate

Parsing establishes whether the request is valid JSON. Schema validation establishes whether the resulting value satisfies the contract.

Neither proves that the product exists, stock is available, or the user may place the order. Those checks depend on application state and authorization.

A quantity of 50 may be structurally valid while exceeding available stock. Keeping these stages distinct makes errors easier to explain and prevents “schema valid” from becoming shorthand for “safe to execute.”

The same distinction matters for API mocks: a plausible response shape does not prove the business workflow works.

Validator Configuration Is Part of the Contract

Choose a validator that supports the declared schema dialect.

Also understand configuration affecting coercion, default insertion, and removal of extra properties. Rejecting an unexpected field is different from silently deleting it.

String formats need particular care. The JSON Schema format documentation explains that format can operate as an annotation rather than an assertion. Writing "format": "email" is not enough to assume every validator enforces it.

For the order endpoint, document the actual acceptance behavior alongside the schema.

Avoid Two Independently Edited Definitions

A team can start with JSON Schema and generate TypeScript declarations, or use a runtime schema tool that supplies inferred static types.

Ajv’s TypeScript integration also supports typed validation functions that narrow values after successful checks, with documented limits on its type utilities.

Choose an authoritative representation and review the generated result. Handwritten assertions around a validator can still promise more than the schema checks.

When the contract changes, contract and integration testing address different compatibility questions across consumers and providers.

Do You Need Both?

A TypeScript application needs runtime validation wherever external values can violate its assumptions. JSON Schema is one way to supply that validation, especially when contracts must cross language or tooling boundaries.

Values created entirely within well-typed code do not necessarily need repeated schema checks between every function.

For the order endpoint, the useful boundary is clear: parse and validate the incoming request, then let typed application code work with the accepted value. The two mechanisms complement each other without requiring two manually maintained contracts.

A type assertion changes what the compiler believes, not what arrived over the network.
Top