Generate TypeScript types and proxy classes from example JSON objects. Type check JSON objects at runtime!
With MakeTypes, you can interact with REST endpoints and other sources of JSON with confidence that your program receives the data you expect. All you need is a file containing representative sample JSON objects.
Features:
npm i -g maketypes
npm install
First, write a file called
samples.json that contains JSON samples for a particular object type, such as the value returned from a web service. It can either contain a single sample or an array of samples.
Then, run:
make_types -i interfaces.ts -p proxies.ts samples.json RootName
...where:
interfaces.tswill hold interface definitions for the JSON (optional)
proxies.tswill hold proxy class definitions that you can use to dynamically type check JSON objects at runtime (optional)
RootNamespecifies the name to use for the type that describes the JSON object
MakeTypes will use simple heuristics to determine the names of nested sub-types.
MakeTypes generates proxy classes that dynamically check that runtime JSON objects match the expected static type.
They also standardize optional/nullable fields to contain
null rather than
null or
undefined, which simplifies
your code.
Example
samples.json:
Generation command:
make_types -p proxies.ts samples.json RootName
Proxy generated from example:
export class RootNameProxy {public readonly foo: string;public readonly baz: number | null;public static Parse(d: string): RootNameProxy {return RootNameProxy.Create(JSON.parse(d));}public static Create(d: any): RootNameProxy {if (d === null || d === undefined) {throwNull2NonNull("RootName");} else if (typeof(d) !== 'object') {throwNotObject("RootName");} else if (Array.isArray(d)) {throwIsArray("RootName");}return new RootNameProxy(d);}private constructor(d: any) {checkString(d.foo, false);this.foo = d.foo;checkNumber(d.baz, true);if (d.baz === undefined) {d.baz = null;}this.baz = d.baz;}}
Example TypeScript code using proxy class:
import {RootNameProxy} from './proxies';// RootName.Create will throw an exception if rawJson does not match the type of RootName.const proxyObject = RootNameProxy.Parse('{"foo": "bar"}');// Now, you can access all of the properties of the JSON object with confidence that they// actually exist.let foo = proxyObject.foo; // TypeScript knows foo is a string// If one of the properties on the proxy is optional, then it will have a null value.let baz = proxyObject.baz; // TypeScript knows baz is number | null. In this case, it will be null.
For a lighterweighter option that provides no runtime guarantees, MakeTypes can also generate TypeScript interfaces that describe the expected structure of JSON objects. You can use these interfaces to typecheck code that interacts with JSON objects, but they cannot check if the JSON objects obey the static type at runtime.
Interfaces also succinctly express the static type that MakeTypes is inferring from your samples, so this feature can be a good debugging mechanism.
Example
samples.json:
Generation command:
make_types -i interfaces.ts samples.json RootName
Interface generated from example:
export interface RootName {foo: string;baz?: number | null;}
Example TypeScript code using interfaces:
import {RootName} from './interfaces';const rawJson = <RootName> JSON.parse('{"foo": "bar"}');let foo = rawJson.foo; // TypeScript knows foo i a stringlet baz = rawJson.baz; // TypeScript knows that baz is an optional field that may not be there.
MakeTypes uses the Common Preferred Shape Relation from Petricek et al.'s PLDI 2016 paper, "Types from Data: Making Structured Data First-Class Citizens in F#".
Since JavaScript/TypeScript lacks a distinction between integer and floating point types, we merge the
float and
int types into a
number type.