thomasaribart / json-schema-to-ts Goto Github PK
View Code? Open in Web Editor NEWInfer TS types from JSON schemas 📝
License: MIT License
Infer TS types from JSON schemas 📝
License: MIT License
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.
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?
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).
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>;
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
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
:
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>;
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>>>;
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!
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?
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`
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.
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?
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?
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:
v16.16.0
v4.7.4
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?
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
json-schema-to-ts/builds/deno/index.d.ts
Line 307 in d8faafb
json-schema-to-ts/builds/deno/index.d.ts
Line 312 in d8faafb
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!
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>
Hoping someone can shed some light on this for me. Hoping I'm making a simple mistake somewhere
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.
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.
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.
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 !
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.
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.
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>;
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;
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?
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)
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
}
}>
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.
json-schema-to-ts version: 1.6.4
typescript version: 4.4.3
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
FromSchema
does not infer required properties when compilerOptions.keyofStringsOnly = true
.
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
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;
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.devHow to fix:
- If you believe this to be an error in Skypack, file an issue here: https://github.com/skypackjs/skypack-cdn/issues
- If you believe this to be an issue in the package, share this URL with the package authors to help them debug & fix.
- Use https://skypack.dev/ to find a web-friendly alternative to find another package.
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:
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! ❤️
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.
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.
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.
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"
}
}
}
}
}
}
}```
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:
FromSchema
type or two from other filestsc
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!
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:
json-schema-to-ts
on-hover:
I have created a JSON schema that goes quite deep.
Now when I add a simple property like below anywhere within the schema, the last part of the anyOf
gets omitted:
{
"properties": {
"test": {
"type": "string"
}
}
}
I haven't used JSON Schemas much. Using the validator: https://www.jsonschemavalidator.net/ worked out fine.
Not sure if I'm doing something wrong on my end.
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!
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, ... }
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" }
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?
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?
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
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?
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
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
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 !
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.