Code Monkey home page Code Monkey logo

json-schema-to-ts's People

Contributors

azizghuloum avatar dariocravero avatar dependabot[bot] avatar fargito avatar github-actions[bot] avatar javivelasco avatar ngruychev avatar stalniy avatar thomasaribart 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  avatar

json-schema-to-ts's Issues

Deno compatibility

Hi! Is it possible to use this library from Deno? I tried this:

import { FromSchema } from "https://cdn.skypack.dev/json-schema-to-ts";
// or
// import { FromSchema } from "https:/jspm.dev/json-schema-to-ts";

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

type Foo = FromSchema<typeof fooSchema>;

but got:

►  deno run json-schema-to-ts.ts
Check file:///Users/alexey.alekhin/github/tapad/github-actions-ci-dev/json-schema-to-ts.ts
error: TS2614 [ERROR]: Module '"https://cdn.skypack.dev/json-schema-to-ts"' has no exported member 'FromSchema'. Did you mean to use 'import FromSchema from "https://cdn.skypack.dev/json-schema-to-ts"' instead?
import { FromSchema } from "https://cdn.skypack.dev/json-schema-to-ts";
         ~~~~~~~~~~

or with jspm:

►  deno run json-schema-to-ts.ts
error: An unsupported media type was attempted to be imported as a module.
  Specifier: https://jspm.dev/npm:json-schema-to-ts/~
  MediaType: Unknown

I wonder if adding exports field in the package.json could help those CDNs to process it correctly?


Also, I got this message from skypack:

[Package Error] "[email protected]" could not be built.

[1/5] Verifying package is valid…
[2/5] Installing dependencies from npm…
[3/5] Building package using esinstall…
Running esinstall...
No ESM dependencies found!
At least one dependency must have an ESM "module" entrypoint. You can find modern, web-ready packages at https://www.skypack.dev
No ESM dependencies found!
At least one dependency must have an ESM "module" entrypoint. You can find modern, web-ready packages at https://www.skypack.dev

How to fix:

Can deserialization patterns use the property name?

I'm trying to use a custom deserialization to type check CSS in my JSON objects.

I'm using a CSS type fromcsstype:

import type * as CSS from "csstype"; 
type CustomStyle = CSS.Properties<string | number>;

And have the CSS as a property called "customStyle" in my schemas:

const schemaWithCustomStyle =  {
  type: "object",
  properties: {
    customStyle: {
      type: "object",
    },
  },
  additionalProperties: false,
} as const;

The type I would like to get from this is something like

type TypeWithCustomStyle = {
    customStyle?: CustomStyle;
}

However since there's not enough information in the type to match a pattern:

type TypeWithCustomStyle = FromSchema<
  typeof schemaWithCustomStyle,
  {
    deserialize: [
      {
        pattern: {
          type: "object";
        };
        output: CustomStyle;
      }
    ];
  }
>;

matches the outer schema:

type TypeWithCustomStyle = CSS.Properties<string | number, string & {}>

Is there a way to pattern match based on the name of the property, e.g.

type TypeWithCustomStyle = FromSchema<
  typeof schemaWithCustomStyle,
  {
    deserialize: [
      {
        pattern: {
          propertyName: "customStyle";
        };
        output: CustomStyle;
      }
    ];
  }
>;

type TypeWithCustomStyle = {
    customStyle?: CustomStyle;
}

I took a look at the deserialize.ts file:

type RecurseOnDeserializationPatterns<
  S extends JSONSchema7,
  P extends DeserializationPattern[],
  R = M.Any
> = {
  stop: R;
  continue: RecurseOnDeserializationPatterns<
    S,
    L.Tail<P>,
    S extends L.Head<P>["pattern"]
      ? M.$Intersect<M.Any<true, L.Head<P>["output"]>, R>
      : R
  >;
}[P extends [any, ...any[]] ? "continue" : "stop"];

but don't understand how exactly it's working.

allOf operator and ajv-keywords support

Hi, thanks for this library.

I noticed that FromSchema utility does not work with ajv-keywords such as transform: ['trim'] for example. Seems like a bug to me.

image

Code to reproduce:

export const schema = {
  type: 'object',
  properties: {
    prop1: {
      type: 'string',
      allOf: [
        {
          transform: ['trim'],
        },
      ],
    },
  },
  required: ['prop1'],
  additionalProperties: false,
} as const;

type Schema = FromSchema<typeof schema>;

Inferred type is always `never`

Hey,

I am trying the example for the README and I get never as the resulting type :\

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>;
// => Dog is never

I am using Typescript 4.8 on MacOS 12.6 and this is my package.json:

{
  "name": "fastify-ts-typeprovider-demo",
  "version": "0.0.1",
  "description": "",
  "main": "src/server.js",
  "scripts": {
    "start": "nodemon",
    "build": "swc ./src -d built"
  },
  "dependencies": {
    "json-schema-to-ts": "2.5.5"
  },
  "devDependencies": {
    "@swc-node/register": "1.5.4",
    "@swc/cli": "0.1.57",
    "@swc/core": "1.3.10",
    "@types/node": "18.11.3",
    "nodemon": "2.0.20",
    "typescript": "4.8.4"
  },
  "engines": {
    "npm": ">=7.0.0",
    "node": ">=18.11.0"
  }
}

Any idea, what I am doing wrong?

`wrapCompilerAsTypeGuard` and `wrapValidatorAsTypeGuard` are not usable in Deno build

Hello! First off thanks for writing this library! It's awesome and just keeps getting better!

One thing I noticed is in the Deno build the wrapper functions cannot be used because its packaged as only type definitions and the implementations are stripped by rollup

declare const wrapCompilerAsTypeGuard: CompilerWrapper;

declare const wrapValidatorAsTypeGuard: ValidatorWrapper;

Trying to import it and use it throws

error: SyntaxError: The requested module 'https://deno.land/x/[email protected]/index.d.ts' does not provide an export named 'wrapCompilerAsTypeGuard'

I fixed it by just adding the implementation real quick and changing it to a .ts file since Deno doesnt always play nice with .d.ts files. That may not work the best with your rollup setup though and im sure you know a more compatible fix!

Using definition results in unknown type

Version: 1.6.4

Take the following simple schema

export const simpleSchema = {
  $schema: 'https://json-schema.org/draft-2020-12/schema',
  type: 'object',
  properties: {
    exampleObject: {
      type: 'object',
      properties: {
        name: {
          $ref: '#/definitions/example'
        }
      },
      required: ['name'],
      additionalProperties: false
    }
  },
  required: [
    'exampleObject'
  ],
  additionalProperties: false,
  definitions: {
    example: {
      type: 'string'
    }
  }
} as const

When converting this to TS using FromSchema results in the name property being an unknown type.

import { simpleSchema } from './simpleSchema'
type SimpleSchema = FromSchema<typeof simpleSchema>

image

Hoping someone can shed some light on this for me. Hoping I'm making a simple mistake somewhere

allOf doesn't work

Not quite sure how to reproduce this in a useful way, I have a schema like this, which extends a schema in allOf. Even though I have ensure that the accept exists in header in programmatic level, it is not reflected in the type generated by this library.

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

const defHeaderSchema = {
	type: "object",
	properties: {
		header: {
			properties: {
				accept: {
					type: "string",
					pattern: "application/(?:\\*|json)|\\*/\\*"
				}
			},
			required: ["accept"]
		}
	},
} as const

const postSchema = {
	allOf: [ defHeaderSchema ],
	type: "object",
	properties: {
		body: {
			type: "object",
			properties: {
				number: {
					type: "string"
				},
			},
			required: ["number"]
		}
	}
} as const;

let obj!: FromSchema<typeof postSchema>

See the final type by hovering on obj

Possibility to make `as const` unnecessary

Maybe there is a way to remove the necessity of as const. Please view the following example:

type Narrow<T> =
  | T extends Function ? T : never
  | T extends string | number | boolean | bigint ? T : never 
  | T extends [] ? [] : never 
  | { [K in keyof T]: Narrow<T[K]> }

declare function test<T>(a: Narrow<T>): T


const asConst = test(['string', 'literal', 123])
// of type ['string', 'literal', 123]

Play with it in TS Playground

Source: https://twitter.com/hd_nvim/status/1578567206190780417

Recursive ref issue

Taking this file as an example:

import { O } from "ts-toolbelt"
import { FromSchema } from "json-schema-to-ts"

export const leafTypeSchema = {
  type: "string",
  enum: ["string", "number", "boolean"],
} as const
export type LeafType = FromSchema<typeof leafTypeSchema>

export const objTypeSchema = {
  type: "string",
  const: "object",
} as const
export type ObjType = FromSchema<typeof objTypeSchema>

export const leafSchema = {
  type: "object",
  additionalProperties: false,
  properties: {
    id: { type: "string", format: "uuid" },
    key: { type: "string" },
    type: leafTypeSchema,
    name: { type: "string" },
  },
  required: ["id", "key", "type", "name"],
} as const
export type Leaf = FromSchema<typeof leafSchema>

export const objSchema = {
  $id: "objSchema",
  type: "object",
  additionalProperties: false,
  properties: {
    id: { type: "string", format: "uuid" },
    key: { type: "string" },
    type: objTypeSchema,
    name: { type: "string" },
    fields: { type: "array", items: { anyOf: [{ $ref: "#" }, leafSchema] } },
  },
  required: ["id", "key", "type", "name", "fields"],
} as const
export type ObjRaw = FromSchema<typeof objSchema>


export const fieldTypeSchema = {
  anyOf: [leafTypeSchema, objTypeSchema],
} as const
export type FieldType = FromSchema<typeof fieldTypeSchema>

export const fieldSchema = {
  anyOf: [objSchema, leafSchema],
} as const
export type Field = Obj | Leaf

export type Obj = O.Update<ObjRaw, "fields", Array<Field>>

I'm seeing this:
Type of property 'fields' circularly references itself in mapped type '{ id: { type: "primitive"; value: string; isSerialized: false; deserialized: never; }; key: { type: "primitive"; value: string; isSerialized: false; deserialized: never; }; type: { type: "const"; value: "object"; isSerialized: false; deserialized: never; }; name: { ...; }; fields: _$Array<...>; }'. (tsserver 2615)

This was working prior to an update from 1.6 to latest (2.5.5). Recommendation on how to resolve?

AJV format support ?

Hey there!
First of all Whow 🤯 So great utility plugin ! really powerfull and easy to use! you are typescript ninjas !
Great job !

Here is my issue.
I'm using AJV format like this

import addFormat from 'ajv-formats'
import {FromSchema} from 'json-schema-to-ts';

export const validateParameters = (
  parametersData: any,
  schema: any
) => {
  const ajv = new Ajv({
    allErrors: true,
    removeAdditional: 'all',
    coerceTypes: true,
    unicodeRegExp: false,
  });
  addFormat(ajv);
  ...

Here is my schema code:

{
  type: 'object',
  properties: {
 supplierInvoiceFile: {
      type: 'object',
      properties: {
        type: {type: 'string'},
        filename: {type: 'string'},
        contentType: {type: 'string'},
        content: {type: 'binary', contentMediaType: 'application/pdf'},
      },
    },
}
}

and here is the calling code :

const body = parse(event, true) as FromSchema<typeof schema>;  //error 
/**
* Type '{ readonly type: "object"; readonly properties: { readonly isTest: { readonly type: "boolean"; readonly default: false; }; 
*  readonly supplierInvoiceFile: { readonly type: "object"; readonly properties: { readonly type: { readonly type: "string"; }; readonly 
*  filename: { ...; }; readonly contentType: { ...; }; readonly...' does not satisfy the constraint 'JSONSchema'.ts(2344)
**/

As soon as the property content is equal to "binary" it seems that the FromSchema is broken.

Is this a normal behavior as the support for binary type is not provided ? Or am I doing something wrong ?

Thanks for your answer !

Enum doesn't work in TS 4.3.5

I'm not sure if it's just 4.3.5 or something earlier in the 4.x line but enums don't seem to work any more

const mySchema = {
  type: 'string',
  enum: ['one', 'two', 'three'] 
} as const

const MyType = FromSchema<typeof mySchema>
// = `never`

Create query param object from query param list

Hi! First off: thanks for all the effort in creating and maintaining this library!

I have a question that I can't seem to figure out an answer to regarding using this library with express and express-openapi, and that I was hoping you could shed some light on:

In short, I'm wondering if there is a way to create a record from query parameters via FromSchema?

A little more context: the OpenAPI spec requires you to type query parameters as a list of objects. These objects have a name, a type, description, and so forth, but it is a list of spec-like objects. Something like:

const params = [
    {
        name: 'paramName',
        schema: {
            type: 'string',
        },
        in: 'query',
    },
] as const;

For endpoints that accept JSON payloads, using FromSchema to generate types has gone very smoothly, but it seems to be a bit trickier with query parameters. Express types out the request as something like this:

        req: Request<unknown, unknown, unknown, QueryParamObject>,

where QueryParamObject is an object with query param names as keys and corresponding types.

My problem is that I'd like to convert the list of query parameters into something that FromSchema can parse, so that we get a nice generated type based on the schema, but I can't figure out how to do it.

The list is obviously not a schema in and of itself, but it's easy enough to map it so that it becomes the equivalent of a properties object on an object-type schema. However, I keep running into issues with readonly and type inference and I'm just not making any headway. Do you have any suggestions as to what I could do here?

Sorry if this doesn't actually apply to this library. I was not the one who set everything up for the project, so I'm not entirely sure how everything is wired together. I do know, though, that the FromSchema method does a lot of our type conversion and that the internals looked pretty intimidating. It may well be that this is outside of what this package is responsible for , but I thought I'd try this as my first port of call.

Thanks very much for any tips, tricks, and insights you can offer.

Support for MongoDB ObjectId field

I use json schemas to validate my mongodb collections, and convert them to typescript types with json-schema-to-ts. Everything works fantastic.

The problem I have is that mongodb saves its ID fields as ObjectId objects, not plain strings. With the help of the deserialize option I could easily map the value to the ObjectId type like this:

type Task = FromSchema<typeof TaskSchema, {
    deserialize: [{
        pattern: {
            type: 'string',
            description: 'objectId'
        },
        output: ObjectId,
    }],
}>;

The problem is that in order to be a valid validation schema, I cannot assign the type string to an id field. Mongodb forces all id fields to have the type bsonType: 'objectId' and to not be just simple string fields. Since the attribute bsonType is not part of the valid json schema definition, the FromSchema<> conversion obviously throws an error. Is there a way I can circumvent this problem? Maybe extending the JSONSchema type definitions?

I hope there is a solutions, since I really want to work with json-schema-to-ts because of its many advantages.

Schema Definitions Arn't Supported

I was attempting to define a schema definition inside my json schema and i'm not getting type validation for the definition value S. I used the Sample Input to verify against the Reference Json Schema

Sample Input

{
  "dynamodb": {
    "NewImage": {
      "id": {
        "S": "b76e5354-0000-0000-0000-17bd3d223d91"
      }
    }
  }
}

Reference Json Schema

  "definitions": {
    "string": {
      "type": "object",
      "properties": {
        "S": {
          "type": "string"
        }
      },
      "required": [
        "S"
      ]
    }
  },
  "required": [
    "dynamodb"
  ],
  "type": "object",
  "properties": {
    "dynamodb": {
      "type": "object",
      "properties": {
        "NewImage": {
          "required": [
            "id"
          ],
          "type": "object",
          "properties": {
            "id": {
              "$ref": "#/definitions/string"
            }
          }
        }
      }
    }
  }
}```

Any limits on the size of the schema?

Really nice project, thank you!

Anyone tried it with large schemas and seen 1) performance degradation or 2) tsc using more relaxed typings because of the size?

support of nullable keyword.

As we know, AJV by default support nullable: true property.
But if we try the same in FromSchema, it's not working.

Is there any way we can work with this?

`Unexpected token 'export'` when exporting `wrapValidatorAsTypeguard` with a ts-node build

Hello, thanks for writing this fantastic library, it has been useful so far!

I'm getting the following error when building with ts-node:

$ cross-env NODE_ENV=development nodemon --exec ts-node -r dotenv-flow/config -r tsconfig-paths/register src/index.ts | pino-pretty -c
[nodemon] 2.0.19
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node -r dotenv-flow/config -r tsconfig-paths/register src/index.ts`
/home/lcsmuller/PF/quickstart-nodejs-rest/node_modules/json-schema-to-ts/lib/index.js:1
export { wrapCompilerAsTypeGuard, wrapValidatorAsTypeGuard, } from "json-schema-to-ts/lib/type-guards";
^^^^^^

SyntaxError: Unexpected token 'export'
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1033:15)
    at Module._compile (node:internal/modules/cjs/loader:1069:27)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Object.require.extensions.<computed> [as .js] (/home/lcsmuller/PF/quickstart-nodejs-rest/node_modules/ts-node/src/index.ts:1361:43)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/home/lcsmuller/PF/quickstart-nodejs-rest/src/common/schemas/utils/helpers.ts:3:1)
[nodemon] app crashed - waiting for file changes before starting...

This error only seems to trigger when I attempt to export a method that comes from a .js file (wrapValidatorAsTypeguard or wrapCompilerAsTypeguard)... It appears that ts-node cannot support export and import syntax by default, I think an easy fix for this would be targeting es5 instead (unless there is a rationale for not doing so). If you are busy, I can give it a try to write the PR.

Question: what is the performance impact of implementing this in the typing system?

Hi @ThomasAribart, thanks for your awesome work, json-schema-to-ts looks very interesting!

I was wondering though, what performance impact can we expect implementing this in the typing system? A lot of TS logic needs to happen on the fly for inferring the schema TS types, and my worry is that when codebases grow, the TypeScript language server might take a long time to startup, and we'll start feeling in in our editing experience. But maybe it's all super fast and little work compared to all other things TS has to do on a codebase.

I imagine a few use cases where this matters:

  • opening your editor, lang server starts, opening a file that uses a FromSchema type or two from other files
  • opening your editor, lang server starts, opening a file with many/all schema's in your app
  • building your project with tsc

I'm now using a (more complex) build time solution, but if json-schema-to-ts has an insignificant perf impact it would make things easier and more elegant. Curious about your thoughts on this!

[bug] anyOf/union types not working with nulls

Summary

anyOf and arrays of types in JSON schemas does not appear to behave correctly with nulls. Instead of unioning the type with null, nothing seems to happen.

Details

json-schema-to-ts version: 1.6.4
typescript version: 4.4.3

Example

import { FromSchema } from 'json-schema-to-ts';
const mySchema = { anyOf: [{ type: "string" }, { type: "null" }] } as const
type MyType = FromSchema<typeof mySchema>
import { FromSchema } from 'json-schema-to-ts';
const mySchema = { type: ["string", "null"] } as const
type MyType = FromSchema<typeof mySchema>

In both these examples, MyType should be string | null, but is instead string

tsc compiled js still imports json-schema-to-ts which imports as a "ts" file

I notice every other library in my node_modules has "main": "index.js" or similar, and import correctly in my project here.

Maybe you could have that file be a no-op in your published packages, and have "main": "index.js" pointing to that no-op, and "typings": "./lib/index.d.ts" instead, so that imports of 'json-schema-to-ts' don't start throwing errors like:

import { JSONSchema6Definition } from "json-schema";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:979:16)
    at Module._compile (internal/modules/cjs/loader.js:1027:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (./dist/src/myfile.js:21:29)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)

There may be a better way here, I'm not the leading expert on JS module resolution.

Schema Schema

Is there a Schema for the Schema?

const fooSchema : JsonSchema = {
  type: "object",
  properties: {
    bar: { type: "number" },
    type: { enum: ["one", "two", "tree"] },
  },
  required: ["bar", "type"],
} as const;

Support for "uri" type?

The following code gives me an error in vscode intellisense:

const inputSchema = {
  type: 'object',
  properties: {
    target: {
      type: 'string',
      minLength: 1,
    },
    url: {
      type: 'uri'
    },
    count: {
      type: 'integer',
      minimum: 0,
    },
    cookie: {
      type: 'string',
    },
  },
  required: ['url', 'count']
} as const

type Task = FromSchema<typeof inputSchema>

The error I get is: Types of property 'type' are incompatible. Type '"uri"' is not assignable to type 'JSONSchema6TypeName | readonly JSONSchema6TypeName[] | undefined'.ts(2344)

But when I change the type for "url" to "string", the problem goes away. Is the "uri" type not supported?

Document compilation performance

This looks like an amazing project! I'd love to build an app around it.

One thing I'm curious about is how slow it might make my TS compilation times as the application grows. Do you have any real-world data of compilation times in large projects? Are you aware of pathological patterns in js schemas that cause slow compilation? Or do you have a theoretical idea of how slow this could get in certain situations?

Thanks!

Provide Repository Changelog

Hi,

thanks for your work!
I noticed that the repository has just been updated to its latest version, moving it from v2.5.2 to v5.2.3.

Would it be possible to provide a CHANGELOG.md file, similar to the one proposed in keepachangelog website to briefly explain the changes that are introduced with each new tag?

Moreover, would it be possibile to describe the reason why repository version moved of three major versions? Is it possible that is just a typo?

Error when using array in examples

It seems that using an array anywhere as part of examples produces a compilation error.

const schema = {
    type: "array",
    items: {
        type: "string"
    },
    examples: [
        ["test"]
    ]
} as const;

type Example = FromSchema<typeof schema>;

This produces a compilation error:

Type '{ readonly type: "array"; readonly items: { readonly type: "string"; }; readonly examples: readonly [readonly ["test"]]; }' does not satisfy the constraint 'JSONSchema'.
  Type '{ readonly type: "array"; readonly items: { readonly type: "string"; }; readonly examples: readonly [readonly ["test"]]; }' is not assignable to type '{ readonly type?: JSONSchema6TypeName | readonly JSONSchema6TypeName[] | undefined; readonly items?: boolean | { readonly $id?: string | undefined; readonly $ref?: string | undefined; ... 35 more ...; readonly format?: string | undefined; } | readonly (boolean | { ...; })[] | undefined; ... 35 more ...; readonly not...'.
    Types of property 'examples' are incompatible.
      Type 'readonly [readonly ["test"]]' is not assignable to type 'readonly (string | number | boolean | { readonly [x: string]: string | number | boolean | ... | { readonly [x: number]: string | number | boolean | ... | ... | null; readonly length: number; readonly toString: {}; readonly toLocaleString: {}; ... 30 more ...; readonly [Symbol.unscopables]: {}; } | null; } | { ...; }...'.
        Type 'readonly ["test"]' is not assignable to type 'string | number | boolean | { readonly [x: string]: string | number | boolean | ... | { readonly [x: number]: string | number | boolean | ... | ... | null; readonly length: number; readonly toString: {}; readonly toLocaleString: {}; ... 30 more ...; readonly [Symbol.unscopables]: {}; } | null; } | { ...; } | null'.
          Type 'readonly ["test"]' is not assignable to type '{ readonly [x: number]: string | number | boolean | { readonly [x: string]: string | number | boolean | ... | ... | null; } | ... | null; readonly length: number; readonly toString: {}; readonly toLocaleString: {}; ... 30 more ...; readonly [Symbol.unscopables]: {}; }'.ts(2344)

Another example. This works fine:

const schema = {
    additionalProperties: true,
    type: "object",
    properties: {
        foo: {
            type: "string"
        }
    },
    examples: [
        {
            foo: "bar",
            someInt: 1
        }
    ]

} as const;

type Example = FromSchema<typeof schema>;

But if I add an array to the example I get a similar compilation error as above:

const schema = {
    additionalProperties: true,
    type: "object",
    properties: {
        foo: {
            type: "string"
        }
    },
    examples: [
        {
            foo: "bar",
            someInt: 1,
            someArray: [] // <-- Type 'readonly []' is not assignable to type '{ readonly [x: number]: string | number | boolean | { readonly [x: string]: string | number | boolean | ... | ... | null; } | ... | null; readonly length: number; readonly toString: {}; readonly toLocaleString: {}; ... 30 more ...; readonly [Symbol.unscopables]: {}; }'.ts(2344)
        }
    ]

} as const;

type Example = FromSchema<typeof schema>;

Using a dictionary with `$ref` to reference predefined types

First, your type mappings here are next level and as much as I've tried to follow along, I'll admit that I've gotten lost. That being said, I wonder if the following approach would work.

Your example of containing references creates a type that contains no reference to the original type:

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

The result is a unique type.

Consider the following example from the JSON Schema site (https://json-schema.org/understanding-json-schema/structuring.html):

{
  "$id": "https://example.com/schemas/address",

  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}
{
  "$id": "https://example.com/schemas/customer",
  "type": "object",
  "properties": {
    "first_name": { "type": "string" },
    "last_name": { "type": "string" },
    "shipping_address": { "$ref": "/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": ["first_name", "last_name", "shipping_address", "billing_address"]
}

If we used a dictionary to map the $ref values, wouldn't the following be possible.

const addressSchema = { ... };
type Address = FromSchema<typeof AddressSchema>;

const customerSchema = { ... };
type customerSchemaDependencies = {
  "/schemas/address": Address
}
type Customer = FromSchemaWithDeps<typeof customerSchema, customerSchemaDependencies>;
// result type has reference to actual Address; instead of a copy

My little tests suggest that a dictionary approach for this would be possible:

type A1 = {};
type A2 = {};

type Deps = {
  foo: A1;
  bar: A2;
}

type Remapper<T, U> = {
  [P in keyof T]: T[P] extends keyof U ? U[T[P]] : T[P];
}

const Test = {
  a1: 'foo',
  a2: 'bar'
} as const

type Remapped = Remapper<typeof Test, Deps>;
/*
result:
type Remapped = {
    readonly a1: A1;
    readonly a2: A2;
}
*/

Is it possible to update FromSchema to use a dictionary in this way or would we need a new mapping type?
Do you have an idea where that mapping could happen? I'd be willing to help, but as I mentioned before I found source rather complex.

Excessive stack depth comparing types 'ParseMixedSchema<?, T>' and 'ParseMixedSchema<?, T>'.

Hello and thank you for this awesome library! (my little project needs TS safety and serializable schemas, so I would be screwed without json-schema-to-ts!)

This error frequently appears in my codebase when TS performs deep inference with a FromSchema type:

Screen Shot 2022-04-01 at 2 59 05 PM

Heres a Youtube video where I demo this code and we see the issue: https://youtu.be/O2xGGEOYYyI?t=438

Somehow TS gets tripped up, and I only see this error when working with JSON-schema-to-ts codebases.

I have not found any way to "TS-Ignore" this, without causing breaks to useful type safety. As you can see in the screenshot, the "number" schema is being TS-ified correctly.

This example is open source here: https://github.com/zerve-app/zerve/blob/main/apps/demo-server/DemoServer.ts#L8-L10

Any advice or help would be greatly appreciated! ❤️

Upgrading to 1.6.x causes "Type instantiation is excessively deep and possibly infinite" errors

Hi there, thanks for the amazing work 🤩 !
I recently updated to latest version of json-schema-to-ts and was welcome with a TS error message Type instantiation is excessively deep and possibly infinite. All information relative to the issue are detailed in fredericbarthelet/typebridge#8

At the moment, I locked version 1.5.x. Could you help on the matter ? Do you know which dependency upgrade from v1.6 might cause this issue ?

Thanks !

Importing JSON from file fails

Hi,

I have a file lets say named myJsonSchema.json and it contains:

{
  "name": "MySchema",
  "type": "object",
  "properties": {
    "foo": {
      "type": "string"
    },
    "bar": {
      "type": "string"
    },
  },
  "required": ["foo"],
  "additionalProperties": false
}

I then import that file and try to use it like so:

import mySchema from './myJsonSchema.json';

type MySchema = FromSchema<typeof mySchema>;

And I get this error:

TS2344: Type '{ name: string; type: string; properties: { foo: { type: string; }; bar: { type: string; }; }; required: string[]; additionalProperties: boolean; }' does not satisfy the constraint 'JSONSchema'.   Type '{ name: string; type: string; properties: { foo: { type: string; }; bar: { type: string; }; }; required: string[]; additionalProperties: boolean; }' is not assignable to type '{ readonly type?: "string" | "number" | "boolean" | "object" | "any" | "array" | "null" | "integer" | readonly JSONSchema6TypeName[] | undefined; readonly allOf?: readonly (boolean | { ...; })[] | undefined; ... 35 more ...; readonly not?: unknown; }'.     Types of property 'type' are incompatible.       Type 'string' is not assignable to type '"string" | "number" | "boolean" | "object" | "any" | "array" | "null" | "integer" | readonly JSONSchema6TypeName[] | undefined'.

I believe this is happening because the JSON is not an object literal that can be const asserted.

Is there any way to make this work without having to define object literals? I think it is a pretty common use case to store your JSON schemas in .json files as this makes them accessible to other tools (like API Gateway validation and Swagger doc generation).

enum: Object.keys resolves as string[] type

Hi,

const my_enum = {
  a: 1,
  b: 2
} as const

type my_enum_type=Array<keyof typeof my_enum>

const mySchema = {
  type: 'array',
  items: {
    type: 'string',
    enum: Object.keys(my_enum)
  }
} as const

type my_enum_schema = FromSchema<typeof mySchema>

my_enum_type != my_enum_schema

Maybe someone could suggest how to fix it?

if i use enum: ["a","b"] all is fine.

JSONSchema type does not support array as default

Defining an array as default value results in a ts-error

import { JSONSchema } from 'json-schema-to-ts';

// Works fine
export const validSchema: JSONSchema = {
  type: 'object',
  properties: {
    name: {
      type: 'string',
      default: 'stan',
    },
  },
  required: ['name'],
  additionalProperties: false,
} as const;

// Results in a ts-error
export const invalidSchema: JSONSchema = {
  type: 'object',
  properties: {
    names: {
      type: 'array',
      items: { type: 'string' },
      default: ['thomas', 'stan'],
    },
  },
  required: ['names'],
  additionalProperties: false,
} as const;

Here is the resulting ts-error:

const invalidSchema: JSONSchema7
Type '{ readonly type: "object"; readonly properties: { readonly names: { readonly type: "array"; readonly items: { readonly type: "string"; }; readonly default: readonly ["thomas", "stan"]; }; }; readonly required: readonly [...]; readonly additionalProperties: false; }' is not assignable to type 'JSONSchema7'.
Types of property 'properties' are incompatible.
Type '{ readonly names: { readonly type: "array"; readonly items: { readonly type: "string"; }; readonly default: readonly ["thomas", "stan"]; }; }' is not assignable to type 'Record<string, JSONSchema7> | { readonly [x: string]: boolean | { readonly $id?: string | undefined; readonly $ref?: string | undefined; readonly $schema?: string | undefined; ... 44 more ...; readonly examples?: readonly unknown[] | undefined; }; } | undefined'.
Types of property 'names' are incompatible.
Type '{ readonly type: "array"; readonly items: { readonly type: "string"; }; readonly default: readonly ["thomas", "stan"]; }' is not assignable to type 'JSONSchema7 | { readonly $id?: string | undefined; readonly $ref?: string | undefined; readonly $schema?: string | undefined; readonly $comment?: string | undefined; ... 43 more ...; readonly examples?: readonly unknown[] | undefined; } | undefined'.
Types of property 'default' are incompatible.
Type 'readonly ["thomas", "stan"]' is not assignable to type 'JSONSchema7Type | { readonly [x: string]: string | number | boolean | ... | { readonly [x: number]: string | number | boolean | ... | ... | null; readonly length: number; readonly toString: {}; ... 32 more ...; readonly [Symbol.unscopables]: {}; } | null; } | { ...; } | undefined'.
Type 'readonly ["thomas", "stan"]' is not assignable to type '{ readonly [x: string]: string | number | boolean | ... | { readonly [x: number]: string | number | boolean | ... | ... | null; readonly length: number; readonly toString: {}; readonly toLocaleString: {}; readonly pop: {}; ... 30 more ...; readonly [Symbol.unscopables]: {}; } | null; }'.
Index signature for type 'string' is missing in type 'readonly ["thomas", "stan"]'.ts(2322)

How to best surface schema descriptions as type documentation?

As traditional types can have docblocks those can get surfaced in IDEs. How can I best surface descriptions to be available on these types?

import type { FromSchema } from 'json-schema-to-ts';

const dogSchema = {
  type: 'object',
  properties: {
    name: { type: 'string', description: "the dogs' name" },
    age: { type: 'integer' },
  },
  required: ['name', 'age'],
} as const;

type Dog = FromSchema<typeof dogSchema>;

// type Dog = {
//   /**
//    * the dogs' name
//    */
//   name: string;
//   age: number;
// };

function addDog(dog: Dog) {
  return dog;
}

addDog({ name: 'buster', age: 18 });

Traditional type on-hover:

Screen Shot 2022-10-14 at 11 43 33 AM

json-schema-to-ts on-hover:

Screen Shot 2022-10-14 at 11 43 21 AM

When the schema is typed as JSONSchema, FromSchema<typeof schema> is never

Hello,

The newly created type from a schema is never if the schema is typed as JSONSchema.

Let me demonstrate with an object example provided in the README.MD

When typed as JSONSchema

In the following example, I denote objectSchema variable as JSONSchema, and it is indeed helpful when I am typing properties.
However, when I declare Object using objectSchema, its value is never.

import type { FromSchema, JSONSchema } from "json-schema-to-ts"

const objectSchema: JSONSchema = {
      type: "object",
      properties: {
            foo: { type: "string" },
            bar: { type: "number" },
      },
      required: ["foo"],
} as const;
type Object = FromSchema<typeof objectSchema>;

Here you can see the type when I hover over Object:
image


When NOT typed as JSONSchema

The very same example, however, works when I don't annotate objectSchema.

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

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

image

I'm really not sure why, and I'm confused as FromSchema is already defined as a generic expecting JSONSchema type, however, resulting in never when a value typed as JSONSchema.

export declare type FromSchema<S extends JSONSchema> = Resolve<ParseSchema<DeepWriteable<S>>>;

Probably all versions above `1.6.4` are not working with `preserveSymlinks` tsconfig option

Hey, this is a cool library I really like the idea of inferring TS types from JSON schemas 👌🏻

Almost all the solutions I found are focused on generating types (via CLIs or code), this library is different & unique.

I found something interesting, all the recent versions (above 1.6.4) of this library are not working for me, I'm pretty sure my config is correct I'm using Typescript v4 and strict mode is enabled.

This is what I receive, the inferring is not working 😕 Used the example in the Readme.

The interesting part is this, it looks like 1.6.4 is the most used version in the last week, so I test it and it's working perfectly!

Can anyone please confirm that versions above 1.6.4 work? or at least the recent one 2.5.5? Maybe I have something wrong on my side, thanks.

This is my config:

  • node v16.16.0
  • Typescript v4.7.4

How to write the type definitions to files?

Hi, thanks for this library.

I'm wondering if there's a way for me to output the FromSchema definition result into a file ?

I only need to transform the json schema and write it to my own typescript file.

Is there a way to tell typescript the schema of nested field ?

I have two schema, you can look at the personSchema and animalSchema
There is 2 type too.
What i want is that the instance filed has People type.
How can i tell that animal.instance has type Person instead of using cast (please take a look at getPerson function)

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

const personSchema = {
  type: "object",
  properties: {
    national: {
      type: "string",
    },
  },
} as const;

const animalSchema = {
  type: "object",
  properties: {
    kind: {
      type: "string",
    },
    instance: personSchema,
  },
} as const;

type Person = FromSchema<typeof personSchema>;
type Animal = FromSchema<typeof animalSchema>;

function getPerson(animal: Animal): Person {
  // return animal.instance;
  return animal.instance as Person;
}

getPerson({
  kind: "human",
  instance: {
    national: "eu",
  },
});

Thank you for a very helpful package!

Issue when importing generated type into another module

Generated types are not recognized when imported in other modules.

export const CarSchema = {
    type: 'object',
    properties: {
        color: {
            type: 'string',
        },
    },
    required: ['color'],
    additionalProperties: false,
} as const;

export type Car = FromSchema<typeof CarSchema>; will result in type Car = unknown in the imported module. If I specify a deserialize option, it transforms into any: type Car = any and the error Type instantiation is excessively deep and possibly infinite appears.
I am aware of this FAQ, but since this CarSchema only has one string attribute, there should not be heavy computations ongoing. Is there maybe something I can do to resolve this issue?

JSON schema "format" property

JSONSchema supports format on type = string and format date-time I want FromSchema to return Date object instead of string.

There may be other custom formats like object-id (MongoDB special type), so what I suggest is to allow users to specify mapping of format value to some scalar type:

type User = FromSchema<typeof schema, {
  format: {
    "date-time": Date,
    "object-id": ObjectId
  }
}>

Question: is there a way to generate interfaces instead of types?

I want to use type-inheritance and if I get this right, you can only do this with interfaces not with types.

Something like this for example:

interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}

interface AddressWithUnit extends BasicAddress {
  unit: string;
}

But because FromSchema<typeof foo> returns a type rather than an interface how would I go about and implement inheritance with this lib?

schema object without a type attribute

Use case: does-json-schema-to-ts-work-on-json-file-schemas

Look at: schema-object-without-a-type-attribute-in-swagger-2-0

const Account = {
	properties: {
		balance: {
			type: 'string',
			description: 'balance in unit WEI, presented with hex string',
			example: '0x47ff1f90327aa0f8e',
		},
		energy: {
			type: 'string',
			description: 'energy in uint WEI, presented with hex string',
			example: '0xcf624158d591398',
		},
		hasCode: {
			type: 'boolean',
			description: 'whether the account has code',
			example: false,
		},
	},
} as const;
type Dog = FromSchema<typeof Account>;

// Now: type Dog = unknown
// Expected: type Dog = { balance, ... }

Does't work on deno?

deno: 1.15.2

import Ajv from "https://esm.sh/[email protected]";

const schema = {
  type: "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "number", "maximum": 3 },
  },
  required: ["foo", "bar"],
} as const;
const schemaString = JSON.stringify(schema);
console.info(schemaString);

const validate = new Ajv().compile(schema);

const test = (obj: any) => {
  if (validate(obj)) console.log("Valid:", obj);
  else console.log("invalid!:", obj, validate.errors);
};

test({ "foo": "abc", "bar": 2 });
test({ "foo": "abc", "bar": 9 });
test({ "foo": "ab", "bar": "2" });

// https://github.com/ThomasAribart/json-schema-to-ts
import { FromSchema } from "https://deno.land/x/[email protected]/index.d.ts";
type FooBar = FromSchema<typeof schema>;
const forbar: FooBar = { "foo": "abc", "bar": "x" };
console.log(forbar);

The code running output as follow, but const forbar: FooBar = { "foo": "abc", "bar": "x" }; should not compile success?

{"type":"object","properties":{"foo":{"type":"string"},"bar":{"type":"number","maximum":3}},"required":["foo","bar"]}
Valid: { foo: "abc", bar: 2 }
invalid!: { foo: "abc", bar: 9 } [
  {
    instancePath: "/bar",
    schemaPath: "#/properties/bar/maximum",
    keyword: "maximum",
    params: { comparison: "<=", limit: 3 },
    message: "must be <= 3"
  }
]
invalid!: { foo: "ab", bar: "2" } [
  {
    instancePath: "/bar",
    schemaPath: "#/properties/bar/type",
    keyword: "type",
    params: { type: "number" },
    message: "must be number"
  }
]
{ foo: "abc", bar: "x" }

Issue with CompilerOptions.keyofStringsOnly = true

The issue

FromSchema does not infer required properties when compilerOptions.keyofStringsOnly = true.

The reason

I'm working on a project where ts compiler option keyofStringsOnly is set to true. This makes keyof WHATEVER to return string type instead of string | number | symbol. That's why when you check whether number extends keyof S["required"] on this line, it return false.

In general, keyof operator is not safe to test whether type is an array or tuple because even for objects and interfaces it returns string | number.

Also if you check the output type of type indexesOfArray = keyof string[], you will see that ts returns number and names of all
methods. Even keyof ReadonlyArray<string> because it contains numeric indexes, length and all non mutating array methods

How to fix

In order to fix the issue, you need to convert that and similar checks to check on ReadonlyArray<string> or on { length: number }

type GetRequired<S> = "required" extends keyof S
  ? S["required"] extends ReadonlyArray<string>  // <----
    ? S["required"][number]
    : never
  : never;

Typescript Error when using allOf

This is my schema:

{
	title: 'Website Meta Tags Body',
	type: 'object',
	properties: {
		WebsiteID: {
			type: 'integer',
			min: 0
		},
		Body: {
			description: 'Our required Body is Array of object.',
			type: 'array',
			items: {
				type: 'object',
				description: 'In this object we have Type key and based on Type key we have 2 more keys MetaTagID and Body, So we added conditions for that.',
				properties: {
					Type: {
						type: 'string',
						enum: [
							'INSERT',
							'UPDATE',
							'DELETE'
						]
					},
					MetaTagID: {
						type: 'integer',
						min: 1
					},
					Body: {
						type: 'object',
						properties: {
							Name: {
								type: 'string',
								minLength: 1
							},
							Content: {
								type: 'string',
								minLength: 1
							}
						},
						required: [
							'Name',
							'Content'
						]
					}
				},
				required: [
					'Type'
				],
				allOf: [
					{
						if: {
							properties: {
								Type: {
									const: 'INSERT'
								}
							},
							required: [
								'Type'
							]
						},
						then: {
							required: [
								'Body'
							]
						}
					},
					{
						if: {
							properties: {
								Type: {
									const: 'UPDATE'
								}
							},
							required: [
								'Type'
							]
						},
						then: {
							required: [
								'Body',
								'MetaTagID'
							]
						}
					},
					{
						if: {
							properties: {
								Type: {
									const: 'DELETE'
								}
							},
							required: [
								'Type'
							]
						},
						then: {
							required: [
								'MetaTagID'
							]
						}
					}
				]
			},
			minItems: 1
		}
	},
	required: [
		'WebsiteID',
		'Body'
	],
	additionalProperties: false
}

When used FromSchema, Typescript gives some long error, unable to understand the exact error, but after removing allOf it's working properly.

Does anyone of you have any workaround for this issue?

Error: Excessive stack depth comparing types 'ParseMixedSchema<?, T>' and 'ParseMixedSchema<?, T>'.ts(2321)

As of version 1.6.5, I now get this error in several places in my code.

Excessive stack depth comparing types 'ParseMixedSchema<?, T>' and 'ParseMixedSchema<?, T>'.ts(2321)

It comes from this:

import type { FromSchema } from 'json-schema-to-ts';
import createHttpError, { NamedConstructors } from 'http-errors';
import { URLSearchParams } from 'url';

export type ValidatedAPIGatewayProxyEvent<S> = APIGatewayProxyEvent &
  FromSchema<S>;

which I adopted from the Serverless Framework TS template.

I create a JS/JSON API Gateway schema, e.g.:

const schema = {
  type: 'object',
  properties: {
    body: {
      type: 'object',
      properties: {
        coupon_id: { type: 'string' },
        end_of_term: { type: 'boolean' },
        plan_id: { type: 'string' },
        subscription_id: { type: 'string' },
        reactivate: { type: 'boolean' },
        first_payment: { type: 'boolean' },
      },
      required: ['end_of_term', 'plan_id', 'subscription_id'],
      additionalProperties: false,
    },
  },
} as const;

and pass it to Sentry's wrapHandler:

export const handler = Sentry.AWSLambda.wrapHandler<
      ValidatedAPIGatewayProxyEvent<S>,
      APIGatewayProxyResult | undefined
    >(...

This has worked fine up until the latest release.

How to type input for `FromSchema` correctly?

I have the following type of a helper function. I am trying to use JSONSchema here to fulfil the constraint of FromSchema.

type MkMiddy = <Res>(handler: Handler, inputSchema?: JSONSchema) => Lambda.ValidatedAPIGatewayProxyHandlerV2<FromSchema<typeof inputSchema>, Res>

However, Typescript is complaining for this:

error TS2589: Type instantiation is excessively deep and possibly infinite.

Based on the doc, https://github.com/ThomasAribart/json-schema-to-ts/blob/HEAD/documentation/FAQs/can-i-assign-jsonschema-to-my-schema-and-use-fromschema-at-the-same-time.md, it is incorrect to use both types together, so what type should I provide there instead? unknown doesn't work

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.