JSON 06 ๐ JSON Schema Fundamentals and Type Declarations
JSON Schema is a declarative language for describing the structure, the constraints, and the semantics of a JSON document. It is itself written in JSON, and it is used to validate the instances, to generate the documentation, and to produce the TypeScript types. The specification is split into two parts: the Core, which defines the basic foundation and the vocabulary system, and the Validation, which defines the keywords that constrain the instance values . The current version is 2020-12, and the schema is identified by the $schema keyword that points to the meta-schema URL .
The schema is not a type system in the programming-language sense. It is a set of assertions that the instance must satisfy, and the assertions are the keywords. The type keyword asserts that the instance is one of the six core types: object, array, number, string, boolean, or null . The properties keyword asserts that the object’s properties match the given subschemas, and the required keyword asserts that the named properties are present . The items keyword asserts that the array’s elements match the given subschema . The keywords are composable, and the allOf, anyOf, oneOf, and not are the mechanisms that combine the subschemas.
Key point: JSON Schema 2020-12 is the current version, split into the Core and the Validation vocabularies. The type keyword asserts the core type, the properties and the required constrain the objects, and the items constrains the arrays. The json-schema-to-typescript package generates the TypeScript declarations from the schema, and the generated types are the bridge between the runtime validation and the compile-time type safety.
Why JSON Schema exists
The validation problem. A JSON document is just text, and the structure is implicit. A schema makes the structure explicit, and the validator checks that the instance satisfies the assertions. The validation is the mechanism that keeps the malformed data out of the application, and it is the same mechanism that the API gateways and the form libraries use.
The documentation problem. The schema is a machine-readable description of the data, and the documentation tools can render it as the human-readable reference. The title and the description keywords are the annotations, and the annotations are the mechanism that attaches the documentation to the schema . The schema is the single source of truth for the structure and the documentation.
The type-generation problem. The schema is the runtime description, and the TypeScript types are the compile-time description. The json-schema-to-typescript package compiles the schema into the TypeScript declarations, and the generated types are the mechanism that keeps the runtime and the compile-time in sync . The schema is the source, and the types are the derived artifact.
The trade-off. The schema is the extra artifact to maintain, and the schema can drift from the code that uses it. The drift is the cost of the single source of truth, and the code generation is the mechanism that keeps the drift visible. The schema is the contract, and the generated types are the enforcement.
a. The core keywords and the type declarations
The type keyword is the assertion that the instance is one of the six core types. The value can be a string or an array of strings, and the array is the union of the types.
// The type keyword
{
"type": "string"
}
// The union type
{
"type": ["string", "null"]
}
The properties keyword is the assertion that the object’s properties match the subschemas. The required keyword is the assertion that the named properties are present. The additionalProperties keyword is the assertion that the unspecified properties are allowed or rejected .
// The object schema
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"],
"additionalProperties": false
}
The items keyword is the assertion that the array’s elements match the subschema. The minItems and the maxItems are the length constraints, and the uniqueItems is the uniqueness constraint .
// The array schema
{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 10
}
The enum keyword is the assertion that the instance is one of the listed values, and the const is the assertion that the instance is the single value. The pattern is the assertion that the string matches the regular expression, and the format is the semantic assertion for the date, the email, and the other well-known formats .
b. The type generation with json-schema-to-typescript
The json-schema-to-typescript package compiles the schema into the TypeScript declarations. The CLI is json2ts, and the API is the compile and compileFromFile functions .
# The CLI
json2ts -i schema.json -o types.d.ts
# The API
npm install json-schema-to-typescript
The compile function accepts the schema object and the type name, and it returns the TypeScript source. The compileFromFile reads the schema from the file, and the options control the output style and the strictness .
// The compile function
import { compile } from "json-schema-to-typescript";
const schema = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
},
required: ["name"],
};
const types = await compile(schema, "User");
// export interface User {
// name: string;
// age?: number;
// }
The additionalProperties: false option is the mechanism that tells the generator to omit the index signature. The unknownAny option is the mechanism that uses unknown instead of any, and the declareExternallyReferenced is the mechanism that declares the external schemas referenced by the $ref .
// The options
const types = await compile(schema, "User", {
additionalProperties: false,
unknownAny: true,
declareExternallyReferenced: true,
});
The generated types are the compile-time bridge, and the schema is the runtime source. The two are the same contract, and the generation is the mechanism that keeps them aligned.
Complete Example Session
// ============================================
// PART 1: THE BASIC SCHEMA
// ============================================
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
}
// The schema describes the object.
// The name is required.
// The age is optional and non-negative.
// ============================================
// PART 2: THE TYPE KEYWORD
// ============================================
{ "type": "string" }
{ "type": ["string", "null"] }
// The type is the core assertion.
// The array is the union.
// ============================================
// PART 3: THE OBJECT KEYWORDS
// ============================================
{
"type": "object",
"properties": { "email": { "type": "string", "format": "email" } },
"required": ["email"],
"additionalProperties": false
}
// The properties is the subschema map.
// The required is the presence assertion.
// The additionalProperties is the strictness.
// ============================================
// PART 4: THE ARRAY KEYWORDS
// ============================================
{
"type": "array",
"items": { "type": "number" },
"minItems": 1
}
// The items is the element schema.
// The minItems is the length assertion.
// ============================================
// PART 5: THE ENUM AND CONST
// ============================================
{ "enum": ["active", "inactive"] }
{ "const": "fixed" }
// The enum is the closed set.
// The const is the single value.
// ============================================
// PART 6: THE COMPOSITION
// ============================================
{
"allOf": [
{ "type": "object", "properties": { "a": { "type": "string" } } },
{ "type": "object", "properties": { "b": { "type": "number" } } }
]
}
// The allOf is the conjunction.
// The anyOf is the disjunction.
// The oneOf is the exclusive disjunction.
// The not is the negation.
// ============================================
// PART 7: THE REFERENCE
// ============================================
{
"type": "object",
"properties": {
"address": { "$ref": "#/$defs/address" }
},
"$defs": {
"address": { "type": "object", "properties": { "city": { "type": "string" } } }
}
}
// The $ref is the reuse mechanism.
// The $defs is the definitions container.
// ============================================
// PART 8: THE TYPE GENERATION
// ============================================
// json2ts -i schema.json -o types.d.ts
// The generated TypeScript:
// export interface User {
// name: string;
// age?: number;
// }
// ============================================
// PART 9: THE GENERATED TYPE OPTIONS
// ============================================
// The additionalProperties: false option
// omits the index signature.
// The unknownAny: true option uses unknown.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The $schema points to the 2020-12 meta-schema.
// [ ] The type keyword is set.
// [ ] The properties and the required are set for the objects.
// [ ] The items is set for the arrays.
// [ ] The $defs and the $ref are used for the reuse.
// [ ] The json-schema-to-typescript is installed for the types.
// [ ] The generated types are committed or generated in the build.
The ten parts covered the basic schema, the type keyword, the object keywords, the array keywords, the enum and const, the composition, the reference, the type generation, the generated type options, and the checklist. The through-line is that the schema is the runtime contract, and the generated types are the compile-time bridge.
Quick Reference
The core types
| Type | Description |
|---|---|
object | The unordered string-keyed map |
array | The ordered list |
number | The arbitrary-precision decimal |
string | The Unicode sequence |
boolean | The true or false |
null | The null value |
The validation keywords
| Keyword | Applies to | Purpose |
|---|---|---|
type | Any | The core type assertion |
properties | Object | The property subschemas |
required | Object | The required property names |
additionalProperties | Object | The unspecified property policy |
items | Array | The element subschema |
minItems / maxItems | Array | The length constraints |
enum | Any | The closed value set |
const | Any | The single value |
pattern | String | The regex constraint |
format | String | The semantic format |
The composition keywords
| Keyword | Meaning |
|---|---|
allOf | All the subschemas must validate |
anyOf | At least one subschema must validate |
oneOf | Exactly one subschema must validate |
not | The subschema must not validate |
The type generation
| Tool | Command | Purpose |
|---|---|---|
json-schema-to-typescript | json2ts -i schema.json -o types.d.ts | The CLI generation |
compile | compile(schema, "User") | The API generation |
compileFromFile | compileFromFile("schema.json") | The file-based generation |
Best Practices
โ Do This:
// Use the $schema keyword
{ "$schema": "https://json-schema.org/draft/2020-12/schema" } // โ
// Use the required for the mandatory fields
{ "required": ["name"] } // โ
// Use the additionalProperties: false for the strict objects
{ "additionalProperties": false } // โ
โ Don’t Do This:
// Omit the type keyword
{ "properties": { "name": {} } } // the type is unconstrained // โ
// Use the required without the properties
{ "required": ["name"] } // the name is not described // โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The generated type has an index signature | The additionalProperties is not false | Set additionalProperties: false |
The type is any | The unknownAny is not set | Set unknownAny: true |
The $ref is not resolved | The declareExternallyReferenced is not set | Set the option or inline the schema |
| The schema does not validate | The $schema is missing | Add the $schema keyword |
Real-World Examples
1. The string type
{ "type": "string" }
2. The object type
{ "type": "object", "properties": { "name": { "type": "string" } } }
3. The required
{ "required": ["name"] }
4. The array
{ "type": "array", "items": { "type": "number" } }
5. The enum
{ "enum": ["active", "inactive"] }
6. The $ref
{ "$ref": "#/$defs/address" }
7. The allOf
{ "allOf": [{ "type": "object" }, { "required": ["a"] }] }
8. The type generation
json2ts -i schema.json -o types.d.ts
9. The compile API
const types = await compile(schema, "User");
10. The generated type
export interface User { name: string; age?: number; }
Visual
The Schema and the Instance
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE SCHEMA VALIDATES THE INSTANCE โ
โ โ
โ The schema โ
โ { โ
โ "type": "object", โ
โ "properties": { "name": { "type": "string" } }, โ
โ "required": ["name"] โ
โ } โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The validator runs โ The instance is checked โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโดโโโโโโโ โ
โ โผ โผ โ
โ โโโโโโโโโโ โโโโโโโโโโ โ
โ โ Valid โ โInvalid โ โ
โ โโโโโโโโโโ โโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Type Generation
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE SCHEMA IS THE SOURCE, THE TYPES ARE THE DERIVED โ
โ โ
โ schema.json โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ json-schema-to- โ The compile step โ
โ โ typescript โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ types.d.ts โ The TypeScript declarations โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The application โ The compile-time type safety โ
โ โ imports the types โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Composition
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE COMPOSITION KEYWORDS โ
โ โ
โ allOf: all the subschemas must validate โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ A โง B โง C โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ anyOf: at least one must validate โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ A โจ B โจ C โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ oneOf: exactly one must validate โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ A โ B โ C โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Current version | 2020-12 |
| The core types | object, array, number, string, boolean, null |
| The object keywords | properties, required, additionalProperties |
| The array keyword | items |
| The composition | allOf, anyOf, oneOf, not |
| The type generation | json-schema-to-typescript |
| The CLI | json2ts |
Key takeaways:
- JSON Schema 2020-12 is the current version, split into the Core and the Validation vocabularies. The Core defines the foundation and the vocabulary system, and the Validation defines the keywords that constrain the instance values . The
$schemakeyword points to the meta-schema URL . - The
typekeyword asserts the core type. The six types are object, array, number, string, boolean, and null. The value can be a string or an array of strings for the union . - The
propertiesand therequiredconstrain the objects, and theitemsconstrains the arrays. TheadditionalPropertiesis the strictness policy for the unspecified properties . - The
json-schema-to-typescriptpackage generates the TypeScript declarations. Thecompileand thecompileFromFileare the API, and thejson2tsis the CLI . The generated types are the compile-time bridge. - The schema is the runtime contract, and the types are the compile-time enforcement. The generation is the mechanism that keeps them aligned, and the
additionalProperties: falseis the option that produces the strict types.
Remember: JSON Schema is the declarative language for the JSON structure. The schema is the runtime contract, and the generated TypeScript types are the compile-time bridge. The type keyword is the core assertion, the properties and the required are the object constraints, and the items is the array constraint. The json-schema-to-typescript package generates the types, and the generation is the mechanism that keeps the runtime and the compile-time in sync.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!