JSPM

  • Created
  • Published
  • Downloads 1749165
  • Score
    100M100P100Q191384F
  • License MIT

Infer typescript types from your JSON schemas!

Package Exports

  • json-schema-to-ts

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (json-schema-to-ts) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Stop typing twice 🙅‍♂️

A lot of projects use JSON schemas for runtime data validation along with TypeScript for static type checking.

Their code may look like this:

const dogSchema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "integer" },
    hobbies: { type: "array", items: { type: "string" } },
    favoriteFood: { enum: ["pizza", "taco", "fries"] },
  },
  required: ["name", "age"],
};

type Dog = {
  name: string;
  age: number;
  hobbies?: string[];
  favoriteFood?: "pizza" | "taco" | "fries";
};

Both objects carry similar if not exactly the same information. This is a code duplication that can annoy developers and introduce bugs if not properly maintained.

That's when json-schema-to-ts comes to the rescue 💪

FromSchema

The FromSchema method allows infering TS types directly from JSON schemas:

import { FromSchema } from "json-schema-to-ts";

const dogSchema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "integer" },
    hobbies: { type: "array", items: { type: "string" } },
    favoriteFood: { enum: ["pizza", "taco", "fries"] },
  },
  required: ["name", "age"],
} as const;

type Dog = FromSchema<typeof dogSchema>; // => Will infer the same type as above

Schemas can even be nested, as long as you don't forget the as const statement:

const catSchema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "integer" },
    favoriteThings: { enum: ["playing", "sleeping", "sleepingMore"] },
  },
  required: ["name", "age"],
} as const;

const petSchema = {
  anyOf: [dogSchema, catSchema],
} as const;

type Pet = FromSchema<typeof petSchema>;
// => Will work 🙌

Note: The as const statement is used so that TypeScript takes the schema definition to the word (e.g. true is interpreted as the true constant and not as boolean). It is pure TypeScript and has zero impact on the compiled code.

Docs

Installation

# npm
npm install --save-dev json-schema-to-ts

# yarn
yarn add --dev json-schema-to-ts

Use cases

Litterals

const litteralSchema = {
  type: "null", // "boolean", "string", "integer", "number"
} as const;

type Litteral = FromSchema<typeof litteralSchema>;
// => null, boolean, string or number

Objects

const objectSchema = {
  type: "object",
  properties: {
    foo: { type: "string" },
    bar: { type: "number" },
  },
  required: ["foo"],
} as const;

type Object = FromSchema<typeof objectSchema>;
// => { foo: string, bar?: number }

FromSchema partially supports the use of the additionalProperties and patternProperties keyword:

  • Contrary to the specifications, additionalProperties is considered false by default for clearer typings. Set its value to true to signal that additional properties can be used:
const additionalPropertiesSchema = {
  ...objectSchema,
  additionalProperties: true,
} as const;

type Object = FromSchema<typeof additionalPropertiesSchema>;
// => { [x: string]: any; foo: string; bar?: number }
  • Used on their own, typed additionalProperties and/or patternProperties are supported:
const typedValuesSchema = {
  type: "object",
  additionalProperties: {
    type: "boolean",
  },
} as const;

type Object = FromSchema<typeof typedValuesSchema>;
// => { [key: string]: boolean }

const patternSchema = {
  type: "object",
  patternProperties: {
    "^S": { type: "string" },
    "^I": { type: "integer" },
  },
} as const;

type Object = FromSchema<typeof patternSchema>;
// => { [key: string]: string | number }
  • However, due to TypeScript limitations, when used in combination with the properties keyword, extra properties will always be typed as any to avoid conflicts with base properties.

Arrays

const arraySchema = {
  type: "array",
  items: { type: "string" },
} as const;

type Array = FromSchema<typeof arraySchema>;
// => string[]

Tuples

const tupleSchema = {
  type: "array",
  items: [{ type: "boolean" }, { type: "string" }],
} as const;

type Tuple = FromSchema<typeof tupleSchema>;
// => [] | [boolean] | [boolean, string] | [boolean, string, ...any[]]

FromSchema supports the additionalItems keyword:

  • You can deny additional items:
const tupleSchema = {
  type: "array",
  items: [{ type: "boolean" }, { type: "string" }],
  additionalItems: false,
} as const;

type Tuple = FromSchema<typeof tupleSchema>;
// => [] | [boolean] | [boolean, string]
  • Or specify a type for additional items:
const tupleSchema = {
  type: "array",
  items: [{ type: "boolean" }, { type: "string" }],
  additionalItems: { type: "number" },
} as const;

type Tuple = FromSchema<typeof tupleSchema>;
// => [] | [boolean] | [boolean, string] | [boolean, string, ...number[]]

FromSchema also supports the minItems and maxItems keyword:

const tupleSchema = {
  type: "array",
  items: [{ type: "boolean" }, { type: "string" }],
  minItems: 1,
  maxItems: 2,
} as const;

type Tuple = FromSchema<typeof tupleSchema>;
// => [boolean] | [boolean, string]

Multiple Types

const multipleTypesSchema = {
  type: ["null", "string"],
} as const;

type Tuple = FromSchema<typeof multipleTypesSchema>;
// => null | string

Other properties like required or additionalItems will also work 🙌

const multipleTypesSchema = {
  type: ["array", "object"],
  items: [{ type: "string" }],
  additionalItems: false,
  properties: {
    name: { type: "string" },
    age: { type: "number" },
  },
  required: ["name"],
} as const;

type Tuple = FromSchema<typeof multipleTypesSchema>;
// => [] | [string] | { name: string, age?: number }

Const

const fooSchema = {
  const: "foo",
} as const;

type Foo = FromSchema<typeof fooSchema>;
// => "foo"

Enums

const enumSchema = {
  enum: [true, 42, { foo: "bar" }],
} as const;

type Enum = FromSchema<typeof enumSchema>;
// => true | 42 | { foo: "bar"}

enum can be used concurrently with type.

const enumSchema = {
  type: "string",
  enum: ["foo", "bar", { foo: "bar" }],
} as const;

type Enum = FromSchema<typeof enumSchema>;
// => "foo" | "bar"

AnyOf

const anyOfSchema = {
  anyOf: [
    { type: "string" },
    {
      type: "array",
      items: { type: "string" },
    },
  ],
} as const;

type AnyOf = FromSchema<typeof fooSchema>;
// => string | string[]