Skip to Content
๐Ÿ“– Guide DocumentsEnhanced JSONJSON Schema

typia.json.schemas โ€” emit JSON Schema / OpenAPI from a TypeScript type

If youโ€™ve ever hand-written a JSON Schema or an OpenAPI components block, you know how mechanical it is. typia generates it from the TypeScript type at compile time. Same model as the validators โ€” no extra schema file to maintain.

signatures
namespace json { function schema<T, Version extends "3.0" | "3.1" = "3.1">(): IJsonSchemaUnit<Version, T>; function schemas<Schemas extends unknown[], Version extends "3.0" | "3.1" = "3.1">(): IJsonSchemaCollection<Version, Schemas>; function application<Class extends object, Version extends "3.0" | "3.1" = "3.1">(): IJsonSchemaApplication<Version, Class>; }
FunctionUse case
json.schema<T>()A single top-level schema, plus a components map for any $refs the type pulls in
json.schemas<[T1, T2, ...]>()Multiple top-level types sharing one components map (the common case for an OpenAPI components.schemas)
json.application<Class>()Class methods โ†’ a list of {name, parameters, output} JSON Schemas โ€” handy for framework-neutral tooling that wants per-method schemas without typiaโ€™s LLM-specific config

For the LLM-specific shape (with $defs, additionalProperties: false, strict mode, โ€ฆ), use typia.llm.parameters or typia.llm.application instead.

Version selects the JSON Schema dialect:

  • "3.1" (default) โ€” OpenAPI 3.1, which is JSON Schema 2020-12. Supports tuples, pattern properties, null as a primitive.
  • "3.0" โ€” OpenAPI 3.0 / Swagger compatibility. No tuples, no pattern properties.

Stick with "3.1" unless a downstream consumer demands the older dialect.

First example

hello-schemas.ts
import typia, { tags } from "typia"; interface User { id: string & tags.Format<"uuid">; age: number & tags.Type<"uint32">; email?: string & tags.Format<"email">; } const schemas = typia.json.schemas<[User]>(); // โ†’ an IJsonSchemaCollection with `User` under components.schemas

The output is a plain object โ€” drop it into your OpenAPI spec, hand it to ajv if you must, or save it to disk for tooling that wants schemas at rest.

examples/src/json/schemas.ts
import typia, { tags } from "typia"; export const MemberSchema = typia.json.schemas<[IMember], "3.0">(); interface IMember { /** Unique user ID generated by server. */ id: string & tags.Format<"uuid">; /** Email address of the member. */ email: string & tags.Format<"email">; /** * Age of the member. * * For reference, only adult can be a member. */ age: number & tags.Type<"uint32"> & tags.ExclusiveMinimum<19> & tags.Maximum<100>; }

undefined

export namespace json { export function schemas< Schemas extends unknown[], Version extends "3.0" | "3.1" = "3.1", >(): IJsonSchemaCollection<Version>; }

What gets included

Anything you express in the TypeScript type carries over:

  • tags.Format<"uuid"> โ†’ "format": "uuid"
  • tags.MaxLength<100> โ†’ "maxLength": 100
  • tags.Minimum<0> โ†’ "minimum": 0
  • JSDoc description on a property โ†’ "description": "..."

A few JSON-Schema-only comment tags are also recognized โ€” they donโ€™t affect runtime validation but they show up in the emitted schema:

TagEffect
@title SomethingSets title
@deprecatedSets deprecated: true
@hidden / @internalExcludes the field from the emitted schema
examples/src/json/schemas-comment-tags.ts
import typia, { tags } from "typia"; export const SpecialCommentTagSchema = typia.json.schemas<[Special], "3.1">(); interface Special { /** * Deprecated tags are just used for marking. * * @deprecated * @title Unsigned integer */ type: number & tags.Type<"int32">; /** * Internal tagged property never be shown in JSON schema. * * It even doesn't be shown in other `typia` functions like `assert<T>()`. * * @internal */ internal: number[]; /** * Hidden tagged property never be shown in JSON schema. * * However, it would be shown in other `typia` functions like * `stringify<T>()`. * * @ignore */ hidden: boolean; /** * You can limit the range of number. * * @exclusiveMinimum 19 * @maximum 100 */ number?: number; /** * You can limit the length of string. * * Also, multiple range conditions are also possible. */ string: string & ( | (tags.MinLength<3> & tags.MaxLength<24>) | (tags.MinLength<40> & tags.MaxLength<100>) ); /** * You can limit the pattern of string. * * @pattern ^[a-z]+$ */ pattern: string; /** * You can limit the format of string. * * @format date-time */ format: string | null; /** In the Array case, possible to restrict its elements. */ array: Array<string & tags.Format<"uuid">> & tags.MinItems<3>; }

Custom fields

JSON Schema reserves the x- prefix for non-standard extensions. To embed x-โ€ฆ properties (for vendor-specific tooling, internal flags, anything that isnโ€™t part of the standard), use tags.TagBase.schema or the tags.JsonSchemaPlugin helper:

examples/src/json/schemas-type-tags.ts
import typia, { tags } from "typia"; export const SpecialTypeTagSchema = typia.json.schemas<[IAccount]>(); type Monetary<Value extends string> = tags.TagBase<{ target: "number"; kind: "monetary"; value: Value; schema: { "x-monetary": Value; }; }>; type Placeholder<Value extends string> = tags.JsonSchemaPlugin<{ "x-placeholder": Value; }>; interface IAccount { code: string & Placeholder<"Write your account code please">; balance: number & Monetary<"dollar">; }

Restrictions

bigint is rejected

JSON Schema has no bigint. typia refuses to compile a schema for a type that contains one:

json.schemas.ts
import typia, { tags } from "typia"; interface Something { bigint: bigint; array: bigint[]; nested: Nested; } interface Nested { uint64: bigint & tags.Type<"uint64">; } typia.json.schemas<[Something]>();

Native classes โ€” only Date is allowed

Map, Set, Uint8Array, โ€ฆ are rejected. Date is special-cased: it serializes as string & tags.Format<"date-time">, so typia accepts it.

json.schemas.native.ts
import typia from "typia"; interface Native { date: Date; } typia.json.schemas<[Native]>();

Where to go next

Last updated on