Git Product home page Git Product logo

maketypes's Introduction

MakeTypes 1.1.2

Generate TypeScript types and proxy classes from example JSON objects. Type check JSON objects at runtime!

NPM version Build Status Coverage Status

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.

Try the web demo!

Features:

  • Type-checked JSON.parse. Proxy classes generated with MakeTypes will parse your JSON and check that it matches the expected type during runtime.
  • Statically type-check code that interacts with JSON objects. Proxy objects generated with MakeTypes are expressed as TypeScript classes, so you can statically type check that your code is appropriately accessing fields on the JSON object.
  • Generate TypeScript interfaces to describe JSON types. Don't want the overhead of runtime type checking, and trust that your samples are representative? You can opt to generate TypeScript interfaces instead, which describe the expected structure of the JSON object and facilitate type checking.

Install

npm i -g maketypes

Build

npm install

Usage

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.ts will hold interface definitions for the JSON (optional)
  • proxies.ts will hold proxy class definitions that you can use to dynamically type check JSON objects at runtime (optional)
  • RootName specifies 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.

Using Proxies

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:

[
  {
    "foo": "lalala"
  },
  {
    "foo": "hello",
    "baz": 32
  }
]

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.

Using Interfaces

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:

[
  {
    "foo": "lalala"
  },
  {
    "foo": "hello",
    "baz": 32
  }
]

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 string
let baz = rawJson.baz; // TypeScript knows that baz is an optional field that may not be there.

Inspiration / Technique

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.

maketypes's People

Contributors

abraham avatar eug48 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maketypes's Issues

Labeled top shape

Use TypeScript union types to remove any from all generated interfaces / proxies.

Feature Request: Generate types by the help of existing definitions

Would it be possible to add an existing file with type definitions as input so only not existing types would be generated.

This would be useful if some generated types are adapted by hand and need to be updated later.

Here a example:

input JSON

{
 "prop1":{
    "name":"",
    "area": 500
  },
  "prop2":{
    "name":"",
    "area": 300
  },
  "prop3": {
    "name":"",
    "area": 400
  }
}

MakeTypes output

export interface MyTestInterface {
  prop1: Prop1OrProp2OrProp3;
  prop2: Prop1OrProp2OrProp3;
  prop3: Prop1OrProp2OrProp3;
}
export interface Prop1OrProp2OrProp3 {
  name: string;
  area: number;
}

Adjusted types by hand

export type PropList = 'prop1' | 'prop2' | 'prop3';

export interface MyTestInterface {
[p in PropList]: Prop;
}
export interface Prop {
  name: string;
  area: number;
}

if then the json would be updated to this

{
 "prop1":{
    "name":"",
    "area": 500,
    "population": 10000
  },
   "prop2":{
    "name":"",
    "area": 300,
    "population": 4000
  },
  "prop3": {
    "name":"",
    "area": 400,
    "population": 8000
  }
}

it should generate

export type PropList = 'prop1' | 'prop2' | 'prop3';

export interface MyTestInterface {
[p in PropList]: Prop;
}
export interface Prop {
  name: string;
  area: number;
  population: number;
}

If you think it's possible i could try to dig into the library and try help coding it.

Proxy Classes Have Numeral in Class Name - won't compile

My output's proxy classes are coming out with numeric names like
"export class 3Proxy"
The typescript compiler doesn't accept them - I'm using
"target": "es2015", in tsconfig.json

I'd change them to alpha but there are over 37 sets of them.
The source json is the response from Merriam-Webster Spanish Dictionary lookup,
a very complex json object.

Entity interface name is empty for Union in Array

Not sure how to properly name it. Here is a reproducible example:

Input:

{
    "coord": [
        {
            "lon": 14.42,
            "lat": 50.09
        },
        true
    ]
}

Expected:

export interface Weather {
  coord?: (CoordEntity | boolean)[] | null;
}
export interface CoordEntity {
  lon: number;
  lat: number;
}

Actual output:

export interface Weather {
  coord?: ( | boolean)[] | null;
}
export interface  {
  lon: number;
  lat: number;
}

Can't copy output text

I love this tool and use it often. Usually when I convert the JSON to the interface I just download it, but I've been doing it so often and it's getting annoying. I really just want to be able to copy and paste the output text,but it doesn't work that way.

obj is always null in error message

Hi,

When you use MakeType from your website, the generated Typescript proxies output contain a create function that never initialize the obj object.
So in the error message, the full object is always null...

Error: TypeError: Expected number at root.id but found:
undefined

Full object:
null

That's because there is a default value for the field parameter, so the if(!field) test is always false...

public static Create(d: any, field: string = 'root'): ExampleProxy {
    if (!field) {
      obj = d;
      field = "root";
    }
    .....
}

You should remove the default value and make the parameter optional:

public static Create(d: any, field?: string): ExampleProxy {
    if (!field) {
      obj = d;
      field = "root";
    }
    .....
}

Sugestion: Support ajax data source?

In some scenarios, Maybe I just want to get json from ajax request instead of local json file.
I hope MakeTypes can auto detect data source. if data source is a url, then we can request the url and parse response

the usage maybe look like below:

make_types -i interface.ts https://example.com/data.json RootName

sorry for my poor english, really hope you can understand. thx for your awesome repo.

Recognize embedded types where type Eg = EgEntity | (EgEntity) [];

Some upstream converters from XML to Json, drop the array when it holds only one object.

I notice that
[{ "Eg": 0 }, [{"Eg": 1}] ]
produces the correct output.
export interface EgEntity { Eg: number; } export type Eg = EgEntity | (EgEntity)[] | null;
However, if Eg is inside a number of nested types, it is not recognized and children interfaces become Interface1 and Interface2.

Special characters in field names

Support field names that are not proper JavaScript identifiers.

e.g.: ")hello"

In the non-identifier case, switch to using index [""] notation for accessing properties on JS objects.

Ignore JSON key that contains math operator

I noticed that when I generate the types by pasting JSON that contains "+" on the key, it will automatically pass it into the result, however, "+" is invalid in typescript.

Hope that this can be fixed by ignoring it.

Thanks

Sugestion: Accept multiple sample files

I want to provide multiple samples, but they are in separate files.
Right now I am pre-processing to join the files, but would be very helpful to have this feature.

suggestion: API exposure for program usage

could maketypes expose some api function for programmatically used?

In my case, I have plenty of json files located in different directories like foo/bar, foo/bar/foo, bar/foo/bar, etc, and I want to generate typings per folder, and keep my typing updated with any incoming files. Although I can call maketypes via child process, I prefer to be able to write some code like below:

import { generate } from 'maketypes'

const data: object[] = readDataPerDir('./foo')

const { definitions, proxies } = generate(data, { rootName: 'foo' })

fs.writeFileSync('./foo.d.ts', definitions)

What about a decorator ?

I am not sure typescript do now exports the interfaces, but imagine the following:

@DynamicCast({class: AnInterface})
json : AnInterface;

It would throw an exception if the casting failed, infer the type of json to 'AnInterface' otherwise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.