json-schema-to-ts
Advanced tools
Comparing version 1.2.0 to 1.3.0
{ | ||
"name": "json-schema-to-ts", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Infer typescript types from your JSON schemas!", | ||
@@ -14,3 +14,3 @@ "main": "src/index.ts", | ||
"@types/jest": "^26.0.4", | ||
"ajv": "^6.12.3", | ||
"ajv": "^6.12.6", | ||
"babel-jest": "^26.1.0", | ||
@@ -17,0 +17,0 @@ "jest": "^26.1.0", |
116
README.md
@@ -0,1 +1,7 @@ | ||
<img src="assets/header-round-medium.png" width="100%" align="center" /> | ||
<p align="right"> | ||
<i>If you use this repo, star it ✨</i> | ||
</p> | ||
# Stop typing twice 🙅♂️ | ||
@@ -31,3 +37,3 @@ | ||
# FromSchema | ||
## FromSchema | ||
@@ -57,11 +63,3 @@ The `FromSchema` method lets you infer TS types directly from JSON schemas: | ||
```typescript | ||
const catSchema = { | ||
type: "object", | ||
properties: { | ||
name: { type: "string" }, | ||
age: { type: "integer" }, | ||
favoriteThings: { enum: ["playing", "sleeping", "moreSleeping"] }, | ||
}, | ||
required: ["name", "age"], | ||
} as const; | ||
const catSchema = { ... } as const; | ||
@@ -78,4 +76,64 @@ const petSchema = { | ||
# Docs | ||
## Why use `json-schema-to-ts`? | ||
If you're looking for runtime validation with added types, libraries like [yup](https://github.com/jquense/yup), [zod](https://github.com/vriad/zod) or [runtypes](https://github.com/pelotom/runtypes) may suit your needs while being easier to use! | ||
On the other hand, JSON schemas have the benefit of being widely used, more versatile and reusable (swaggers, APIaaS...). | ||
If you prefer to stick to them and can define your schemas in TS instead of JSON (importing JSONs `as const` is not available yet), then `json-schema-to-ts` is made for you: | ||
- 🙅♂️ **No dependency** | ||
- ✨ **No impact on compiled code**: `json-schema-to-ts` only operates in type space. And after all, what's lighter than a dev-dependency? | ||
- 🍸 **DRYness**: Less code means less embarrassing typos | ||
- 🤝 **Consistency**: See that `string` that you used instead of an `enum`? Or this `additionalProperties` you confused with `additionalItems`? Or forgot entirely? Well, `json-schema-to-ts` does! | ||
- 🔧 **Reliability**: `FromSchema` is extensively tested against [AJV](https://github.com/ajv-validator/ajv), and covers all the use cases that can be handled by TS for now\* | ||
- 🏋️♂️ **Help on complex schemas**: Get complex schemas right first time with instantaneous typing feedbacks! For instance, it's not obvious the following schema can never be validated: | ||
```typescript | ||
const addressSchema = { | ||
type: "object", | ||
allOf: [ | ||
{ | ||
properties: { | ||
street: { type: "string" }, | ||
city: { type: "string" }, | ||
state: { type: "string" }, | ||
}, | ||
required: ["street", "city", "state"], | ||
}, | ||
{ | ||
properties: { | ||
type: { enum: ["residential", "business"] }, | ||
}, | ||
}, | ||
], | ||
additionalProperties: false, | ||
} as const; | ||
``` | ||
But it is with `FromSchema`! | ||
```typescript | ||
type Address = FromSchema<typeof addressSchema>; | ||
// => never 🙌 | ||
``` | ||
> \*If `json-schema-to-ts` misses one of your use case, feel free to [open an issue](https://github.com/ThomasAribart/json-schema-to-ts/issues) 🤗 | ||
## Table of content | ||
- [Installation](#installation) | ||
- [Use cases](#use-cases) | ||
- [Const](#const) | ||
- [Enums](#enums) | ||
- [Primitive types](#primitive-types) | ||
- [Arrays](#arrays) | ||
- [Tuples](#tuples) | ||
- [Objects](#objects) | ||
- [Combining schemas](#combining-schemas) | ||
- [AnyOf](#anyof) | ||
- [AllOf](#allof) | ||
- [OneOf](#oneof) | ||
- [Not and If-Then-Else](#not-and-if-then-else) | ||
## Installation | ||
@@ -91,2 +149,4 @@ | ||
> `json-schema-to-ts` requires TypeScript 3.3+. Activating `strictNullChecks` or using `strict` mode is recommended. | ||
## Use cases | ||
@@ -122,3 +182,3 @@ | ||
Taco = "taco", | ||
Fries = "Fries", | ||
Fries = "fries", | ||
} | ||
@@ -134,10 +194,10 @@ | ||
### Litterals | ||
### Primitive types | ||
```typescript | ||
const litteralSchema = { | ||
const primitiveTypeSchema = { | ||
type: "null", // "boolean", "string", "integer", "number" | ||
} as const; | ||
type Litteral = FromSchema<typeof litteralSchema>; | ||
type PrimitiveType = FromSchema<typeof primitiveTypeSchema>; | ||
// => null, boolean, string or number | ||
@@ -147,11 +207,11 @@ ``` | ||
```typescript | ||
const litteralsSchema = { | ||
const primitiveTypesSchema = { | ||
type: ["null", "string"], | ||
} as const; | ||
type Litterals = FromSchema<typeof litteralsSchema>; | ||
type PrimitiveTypes = FromSchema<typeof primitiveTypesSchema>; | ||
// => null | string | ||
``` | ||
> For `object` and `array` types, properties like `required` or `additionalItems` will apply 🙌 | ||
> For more complex types, refinment keywords like `required` or `additionalItems` will apply 🙌 | ||
@@ -182,3 +242,3 @@ ### Arrays | ||
`FromSchema` supports the `additionalItems` specifications: | ||
`FromSchema` supports the `additionalItems` keyword: | ||
@@ -207,3 +267,3 @@ ```typescript | ||
...as well as the `minItems` and `maxItems` specifications: | ||
...as well as the `minItems` and `maxItems` keywords: | ||
@@ -240,3 +300,3 @@ ```typescript | ||
`FromSchema` partially supports the `additionalProperties` and `patternProperties` specifications: | ||
`FromSchema` partially supports the `additionalProperties` and `patternProperties` keywords: | ||
@@ -251,3 +311,3 @@ - `additionalProperties` can be used to deny additional items. | ||
type Object = FromSchema<typeof additionalPropertiesSchema>; | ||
type Object = FromSchema<typeof closedObjectSchema>; | ||
// => { foo: string; bar?: number; } | ||
@@ -259,3 +319,3 @@ ``` | ||
```typescript | ||
const objectSchema = { | ||
const openObjectSchema = { | ||
type: "object", | ||
@@ -271,3 +331,3 @@ additionalProperties: { | ||
type Object = FromSchema<typeof typedValuesSchema>; | ||
type Object = FromSchema<typeof openObjectSchema>; | ||
// => { [x: string]: string | number | boolean } | ||
@@ -278,2 +338,4 @@ ``` | ||
## Combining schemas | ||
### AnyOf | ||
@@ -292,3 +354,3 @@ | ||
type AnyOf = FromSchema<typeof fooSchema>; | ||
type AnyOf = FromSchema<typeof anyOfSchema>; | ||
// => string | string[] | ||
@@ -400,6 +462,6 @@ ``` | ||
### Not & If/Then/Else | ||
### Not and If-Then-Else | ||
For the same reason as `oneOf` (missing refinment types), I feel like implementing the `not` and the `if/then/else` keywords in `FromSchema` would lead into a rabbit hole... | ||
But I may be wrong ! If you have a use case and you think that it can be implemented, feel free to submit a PR 🤗 | ||
But I may be wrong! If you think that it can be implemented, feel free to [open an issue](https://github.com/ThomasAribart/json-schema-to-ts/issues) 🤗 |
@@ -7,3 +7,3 @@ import { Get } from "../utils"; | ||
import { Enum, EnumType, ResolveEnum } from "./enum"; | ||
import { Litteral, LitteralType, ResolveLitteral } from "./litteral"; | ||
import { Primitive, PrimitiveType, ResolvePrimitive } from "./primitive"; | ||
import { Arr, ArrType, ResolveArr } from "./array"; | ||
@@ -25,3 +25,3 @@ import { Tuple, TupleType, ResolveTuple } from "./tuple"; | ||
| EnumType | ||
| LitteralType | ||
| PrimitiveType | ||
| ArrType | ||
@@ -39,3 +39,3 @@ | TupleType | ||
enum: ResolveEnum<D>; | ||
litteral: ResolveLitteral<D>; | ||
primitive: ResolvePrimitive<D>; | ||
array: ResolveArr<D>; | ||
@@ -54,3 +54,3 @@ tuple: ResolveTuple<D>; | ||
Enum, | ||
Litteral, | ||
Primitive, | ||
Arr, | ||
@@ -57,0 +57,0 @@ Tuple, |
@@ -18,3 +18,3 @@ import { Get } from "../../utils"; | ||
enum: IntersectEnum<B, A>; | ||
litteral: Never; | ||
primitive: Never; | ||
array: IntersectArrs<A, B>; | ||
@@ -21,0 +21,0 @@ tuple: IntersectTuple<B, A>; |
@@ -14,3 +14,3 @@ import { Get, IsObject } from "../../utils"; | ||
enum: CheckExtendsResolved<A, B>; | ||
litteral: CheckExtendsResolved<A, B>; | ||
primitive: CheckExtendsResolved<A, B>; | ||
array: CheckExtendsResolved<A, B>; | ||
@@ -17,0 +17,0 @@ tuple: CheckExtendsResolved<A, B>; |
@@ -14,3 +14,3 @@ import { Get } from "../../utils"; | ||
enum: FilterExtendingResolved<A, B>; | ||
litteral: FilterExtendingResolved<A, B>; | ||
primitive: FilterExtendingResolved<A, B>; | ||
array: FilterExtendingResolved<A, B>; | ||
@@ -17,0 +17,0 @@ tuple: FilterExtendingResolved<A, B>; |
@@ -7,3 +7,3 @@ import { DoesExtend, Get } from "../../utils"; | ||
import { IntersectEnum } from "./enum"; | ||
import { IntersectLitteral } from "./litteral"; | ||
import { IntersectPrimitive } from "./primitive"; | ||
import { ClearArrIntersections, IntersectArr } from "./array"; | ||
@@ -35,3 +35,3 @@ import { ClearTupleIntersections, IntersectTuple } from "./tuple"; | ||
enum: T; | ||
litteral: T; | ||
primitive: T; | ||
array: ClearArrIntersections<T>; | ||
@@ -54,3 +54,3 @@ tuple: ClearTupleIntersections<T>; | ||
enum: IntersectEnum<A, B>; | ||
litteral: IntersectLitteral<A, B>; | ||
primitive: IntersectPrimitive<A, B>; | ||
array: IntersectArr<A, B>; | ||
@@ -57,0 +57,0 @@ tuple: IntersectTuple<A, B>; |
@@ -36,3 +36,3 @@ import { Get, And } from "../../utils"; | ||
enum: IntersectEnum<B, A>; | ||
litteral: Never; | ||
primitive: Never; | ||
array: Never; | ||
@@ -39,0 +39,0 @@ tuple: Never; |
@@ -34,3 +34,3 @@ import { Get, Head, Tail, Prepend, Reverse, And } from "../../utils"; | ||
enum: IntersectEnum<B, A>; | ||
litteral: Never; | ||
primitive: Never; | ||
array: IntersectTupleToArray<A, B>; | ||
@@ -37,0 +37,0 @@ tuple: IntersectTuples<A, B>; |
@@ -21,3 +21,3 @@ import { Get } from "../../utils"; | ||
enum: DistributeIntersection<A, B>; | ||
litteral: DistributeIntersection<A, B>; | ||
primitive: DistributeIntersection<A, B>; | ||
array: DistributeIntersection<A, B>; | ||
@@ -24,0 +24,0 @@ tuple: DistributeIntersection<A, B>; |
@@ -8,3 +8,3 @@ import { Get, Head, Tail, Prepend, Concat, Reverse } from "../utils"; | ||
export type Tuple<V, O = true, P = Any> = { | ||
type: "tuple"; | ||
type: TupleType; | ||
values: V; | ||
@@ -11,0 +11,0 @@ isOpen: O; |
import { Any, Intersection } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, SafeMergeRec, Merge } from "../utils"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
@@ -24,5 +25,5 @@ export type ParseAllOfSchema<S> = RecurseOnAllOfSchema< | ||
Omit<S, "allOf">, | ||
SafeMergeRec< | ||
Merge< | ||
{ properties: {}; additionalProperties: true; required: [] }, | ||
Head<V> | ||
RemoveInvalidAdditionalItems<Head<V>> | ||
> | ||
@@ -29,0 +30,0 @@ > |
import { Intersection, Union } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, SafeMergeRec, Merge } from "../utils"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
@@ -23,3 +24,3 @@ export type ParseAnyOfSchema<S> = Union< | ||
Omit<P, "anyOf">, | ||
SafeMergeRec< | ||
Merge< | ||
{ | ||
@@ -30,3 +31,3 @@ properties: {}; | ||
}, | ||
Head<S> | ||
RemoveInvalidAdditionalItems<Head<S>> | ||
> | ||
@@ -36,5 +37,7 @@ > | ||
> | ||
: ParseSchema<SafeMergeRec<Omit<P, "anyOf">, Head<S>>>) | ||
: ParseSchema< | ||
Merge<Omit<P, "anyOf">, RemoveInvalidAdditionalItems<Head<S>>> | ||
>) | ||
> | ||
: never; | ||
}[S extends [any, ...any[]] ? "continue" : "stop"]; |
@@ -1,2 +0,2 @@ | ||
import { Litteral, Any, Never } from "../meta-types"; | ||
import { Primitive, Any, Never } from "../meta-types"; | ||
@@ -15,6 +15,6 @@ import { ParseConstSchema } from "./const"; | ||
never: Never; | ||
null: Litteral<null>; | ||
boolean: Litteral<boolean>; | ||
number: Litteral<number>; | ||
string: Litteral<string>; | ||
null: Primitive<null>; | ||
boolean: Primitive<boolean>; | ||
number: Primitive<number>; | ||
string: Primitive<string>; | ||
mixed: ParseMixedSchema<S>; | ||
@@ -21,0 +21,0 @@ object: ParseObjectSchema<S>; |
import { Intersection, Union } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, SafeMergeRec, Merge } from "../utils"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
@@ -23,3 +24,3 @@ export type ParseOneOfSchema<S> = Union< | ||
Omit<P, "oneOf">, | ||
SafeMergeRec< | ||
Merge< | ||
{ | ||
@@ -30,3 +31,3 @@ properties: {}; | ||
}, | ||
Head<S> | ||
RemoveInvalidAdditionalItems<Head<S>> | ||
> | ||
@@ -36,5 +37,7 @@ > | ||
> | ||
: ParseSchema<SafeMergeRec<Omit<P, "oneOf">, Head<S>>>) | ||
: ParseSchema< | ||
Merge<Omit<P, "oneOf">, RemoveInvalidAdditionalItems<Head<S>>> | ||
>) | ||
> | ||
: never; | ||
}[S extends [any, ...any[]] ? "continue" : "stop"]; |
48600
47
1189
452