json-schema-to-ts
Advanced tools
Comparing version 1.5.0 to 1.6.0
@@ -0,1 +1,2 @@ | ||
import { A, B } from "ts-toolbelt"; | ||
import { Get } from "../utils"; | ||
@@ -9,1 +10,2 @@ export declare type EnumType = "enum"; | ||
export declare type ResolveEnum<T> = Values<T>; | ||
export declare type IsEnumRepresentable<E> = A.Equals<Values<E>, never> extends B.True ? false : true; |
@@ -12,7 +12,7 @@ import { Get } from "../../utils"; | ||
const: IntersectConst<B, A>; | ||
enum: FilterExtendingResolved<A, B>; | ||
primitive: FilterExtendingResolved<A, B>; | ||
array: FilterExtendingResolved<A, B>; | ||
tuple: FilterExtendingResolved<A, B>; | ||
object: FilterExtendingResolved<A, B>; | ||
enum: FilterUnintersecting<A, B>; | ||
primitive: FilterUnintersecting<A, B>; | ||
array: FilterUnintersecting<A, B>; | ||
tuple: FilterUnintersecting<A, B>; | ||
object: FilterUnintersecting<A, B>; | ||
union: IntersectUnion<B, A>; | ||
@@ -24,4 +24,4 @@ exclusion: IntersectExclusion<B, A>; | ||
}[Get<B, "type"> extends MetaType ? Get<B, "type"> : "errorTypeProperty"]; | ||
declare type FilterExtendingResolved<A, B> = Enum<RecurseOnEnumValues<Values<A>, B>>; | ||
declare type FilterUnintersecting<A, B> = Enum<RecurseOnEnumValues<Values<A>, B>>; | ||
declare type RecurseOnEnumValues<V, B> = V extends infer T ? Intersect<Const<T>, B> extends Never ? never : T : never; | ||
export {}; |
import { DoesExtend, Get } from "../../utils"; | ||
import { Resolve, MetaType, Never, Error } from ".."; | ||
import { ErrorType } from "../error"; | ||
import { IntersectConst } from "./const"; | ||
@@ -11,2 +12,3 @@ import { IntersectEnum } from "./enum"; | ||
import { ClearExclusionIntersections, IntersectExclusion } from "./exclusion"; | ||
import { IsRepresentable } from "../utils"; | ||
export declare type IntersectionType = "intersection"; | ||
@@ -39,3 +41,3 @@ export declare type Intersection<L, R> = { | ||
any: B; | ||
never: Never; | ||
never: Get<B, "type"> extends ErrorType ? B : Never; | ||
const: IntersectConst<A, B>; | ||
@@ -53,1 +55,2 @@ enum: IntersectEnum<A, B>; | ||
}[Get<A, "type"> extends MetaType ? Get<A, "type"> : "errorMissingType"]; | ||
export declare type IsIntersectionRepresentable<A> = IsRepresentable<ClearIntersections<A>>; |
@@ -1,3 +0,5 @@ | ||
import { DoesExtend, Get, DeepMergeUnsafe } from "../utils"; | ||
import { Resolve, Any } from "."; | ||
import { A, B } from "ts-toolbelt"; | ||
import { DoesExtend, Or, Not, Get, DeepMergeUnsafe } from "../utils"; | ||
import { Resolve, Any, Never } from "."; | ||
import { IsRepresentable } from "./utils"; | ||
export declare type ObjectType = "object"; | ||
@@ -12,2 +14,3 @@ export declare type Object<V = {}, R = never, O = true, P = Any> = { | ||
export declare type Values<O> = Get<O, "values">; | ||
export declare type Value<O, K> = K extends keyof Values<O> ? Values<O>[K] : IsOpen<O> extends true ? OpenProps<O> : Never; | ||
export declare type Required<O> = Get<O, "required"> extends string ? Get<O, "required"> : never; | ||
@@ -28,2 +31,6 @@ export declare type IsOpen<O> = Get<O, "isOpen">; | ||
}>>; | ||
declare type IsObjectValueRepresentable<O, K> = K extends keyof Values<O> ? IsRepresentable<Values<O>[K]> : IsOpen<O> extends true ? IsRepresentable<OpenProps<O>> : false; | ||
export declare type IsObjectRepresentable<O> = Or<DoesExtend<A.Equals<Required<O>, never>, B.True>, Not<DoesExtend<false, { | ||
[key in Required<O>]: IsObjectValueRepresentable<O, key>; | ||
}[Required<O>]>>>; | ||
export {}; |
import { Get, Head, Tail, Prepend, Concat, Reverse } from "../utils"; | ||
import { Resolve, Any } from "."; | ||
import { IsRepresentable } from "./utils"; | ||
export declare type TupleType = "tuple"; | ||
@@ -18,2 +19,7 @@ export declare type Tuple<V, O = true, P = Any> = { | ||
}[V extends [any, ...any[]] ? "continue" : "stop"]; | ||
export declare type IsTupleRepresentable<T> = AreAllTupleValuesRepresentable<Values<T>>; | ||
declare type AreAllTupleValuesRepresentable<V> = { | ||
stop: true; | ||
continue: V extends any[] ? IsRepresentable<Head<V>> extends false ? false : AreAllTupleValuesRepresentable<Tail<V>> : never; | ||
}[V extends [any, ...any[]] ? "continue" : "stop"]; | ||
export {}; |
@@ -1,3 +0,4 @@ | ||
import { Get } from "../utils"; | ||
import { DoesExtend, Get } from "../utils"; | ||
import { Resolve } from "."; | ||
import { IsRepresentable } from "./utils"; | ||
export declare type UnionType = "union"; | ||
@@ -9,4 +10,6 @@ export declare type Union<V> = { | ||
export declare type Values<U> = Get<U, "values">; | ||
export declare type ResolveUnion<T> = RecurseOnUnionTree<Values<T>>; | ||
declare type RecurseOnUnionTree<V> = V extends infer T ? Resolve<T> : never; | ||
export declare type ResolveUnion<U> = RecurseOnUnion<Values<U>>; | ||
declare type RecurseOnUnion<V> = V extends infer T ? Resolve<T> : never; | ||
export declare type IsUnionRepresentable<U> = DoesExtend<true, AreUnionValuesRepresentable<Values<U>>>; | ||
declare type AreUnionValuesRepresentable<V> = V extends infer T ? IsRepresentable<T> : never; | ||
export {}; |
import { Any, Intersection } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { Tail, Head, Get, HasKeyIn } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
import { MergeSubSchema } from "./utils"; | ||
export declare type ParseAllOfSchema<S> = RecurseOnAllOfSchema<Get<S, "allOf">, S, HasKeyIn<S, "enum" | "const" | "type" | "anyOf" | "oneOf"> extends true ? ParseSchema<Omit<S, "allOf">> : Any>; | ||
declare type RecurseOnAllOfSchema<V, S, R> = { | ||
stop: R; | ||
continue: V extends any[] ? RecurseOnAllOfSchema<Tail<V>, S, Intersection<ParseSchema<Merge<Omit<S, "allOf">, Merge<{ | ||
properties: {}; | ||
additionalProperties: true; | ||
required: []; | ||
}, RemoveInvalidAdditionalItems<Head<V>>>>>, R>> : never; | ||
continue: V extends any[] ? RecurseOnAllOfSchema<Tail<V>, S, Intersection<ParseSchema<MergeSubSchema<Omit<S, "allOf">, Head<V>>>, R>> : never; | ||
}[V extends [any, ...any[]] ? "continue" : "stop"]; | ||
export {}; |
import { Intersection, Union } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
import { MergeSubSchema, RemoveInvalidAdditionalItems } from "./utils"; | ||
export declare type ParseAnyOfSchema<S> = Union<RecurseOnAnyOfSchema<Get<S, "anyOf">, S>>; | ||
declare type RecurseOnAnyOfSchema<S, P, R = never> = { | ||
stop: R; | ||
continue: S extends any[] ? RecurseOnAnyOfSchema<Tail<S>, P, R | (HasKeyIn<P, "enum" | "const" | "type"> extends true ? Intersection<ParseSchema<Omit<P, "anyOf">>, ParseSchema<Merge<Omit<P, "anyOf">, Merge<{ | ||
properties: {}; | ||
additionalProperties: true; | ||
required: []; | ||
}, RemoveInvalidAdditionalItems<Head<S>>>>>> : ParseSchema<Merge<Omit<P, "anyOf">, RemoveInvalidAdditionalItems<Head<S>>>>)> : never; | ||
continue: S extends any[] ? RecurseOnAnyOfSchema<Tail<S>, P, R | (HasKeyIn<P, "enum" | "const" | "type"> extends true ? Intersection<ParseSchema<Omit<P, "anyOf">>, ParseSchema<MergeSubSchema<Omit<P, "anyOf">, Head<S>>>> : ParseSchema<Merge<Omit<P, "anyOf">, RemoveInvalidAdditionalItems<Head<S>>>>)> : never; | ||
}[S extends [any, ...any[]] ? "continue" : "stop"]; | ||
export {}; |
@@ -11,2 +11,3 @@ import { Primitive, Any, Never } from "../meta-types"; | ||
import { ParseNotSchema } from "./not"; | ||
import { ParseIfThenElseSchema } from "./ifThenElse"; | ||
export declare type ParseSchema<S> = { | ||
@@ -28,4 +29,5 @@ any: Any; | ||
not: ParseNotSchema<S>; | ||
ifThenElse: ParseIfThenElseSchema<S>; | ||
}[InferSchemaType<S>]; | ||
declare type InferSchemaType<S> = S extends true | string ? "any" : S extends false ? "never" : "not" extends keyof S ? "not" : "allOf" extends keyof S ? "allOf" : "oneOf" extends keyof S ? "oneOf" : "anyOf" extends keyof S ? "anyOf" : "enum" extends keyof S ? "enum" : "const" extends keyof S ? "const" : "type" extends keyof S ? S["type"] extends any[] ? "mixed" : S["type"] extends "null" ? "null" : S["type"] extends "boolean" ? "boolean" : S["type"] extends "integer" | "number" ? "number" : S["type"] extends "string" ? "string" : S["type"] extends "object" ? "object" : S["type"] extends "array" ? "array" : "never" : "any"; | ||
declare type InferSchemaType<S> = S extends true | string ? "any" : S extends false ? "never" : "if" extends keyof S ? "ifThenElse" : "not" extends keyof S ? "not" : "allOf" extends keyof S ? "allOf" : "oneOf" extends keyof S ? "oneOf" : "anyOf" extends keyof S ? "anyOf" : "enum" extends keyof S ? "enum" : "const" extends keyof S ? "const" : "type" extends keyof S ? S["type"] extends any[] ? "mixed" : S["type"] extends "null" ? "null" : S["type"] extends "boolean" ? "boolean" : S["type"] extends "integer" | "number" ? "number" : S["type"] extends "string" ? "string" : S["type"] extends "object" ? "object" : S["type"] extends "array" ? "array" : "never" : "any"; | ||
export {}; |
@@ -1,6 +0,8 @@ | ||
import { Exclusion, Union, Any, Primitive, Arr, Object } from "../meta-types"; | ||
import { Get, HasKeyIn, Merge } from "../utils"; | ||
import { Any, Primitive, Arr, Object, Union, Exclusion } from "../meta-types"; | ||
import { IsRepresentable } from "../meta-types/utils"; | ||
import { Get, HasKeyIn } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { MergeSubSchema } from "./utils"; | ||
declare type AllTypes = Union<Primitive<null> | Primitive<boolean> | Primitive<number> | Primitive<string> | Arr<Any> | Object>; | ||
export declare type ParseNotSchema<S> = Exclusion<HasKeyIn<S, "enum" | "const" | "type" | "anyOf" | "oneOf" | "allOf"> extends true ? ParseSchema<Omit<S, "not">> : AllTypes, ParseSchema<Merge<Omit<S, "not">, Get<S, "not">>>>; | ||
export declare type ParseNotSchema<S, P = ParseSchema<Omit<S, "not">>, E = Exclusion<HasKeyIn<S, "enum" | "const" | "type" | "anyOf" | "oneOf" | "allOf"> extends true ? P : AllTypes, ParseSchema<MergeSubSchema<Omit<S, "not">, Get<S, "not">>>>> = IsRepresentable<E> extends true ? E : P; | ||
export {}; |
import { Intersection, Union } from "../meta-types"; | ||
import { Tail, Head, Get, HasKeyIn, Merge } from "../utils"; | ||
import { ParseSchema } from "."; | ||
import { RemoveInvalidAdditionalItems } from "./utils"; | ||
import { MergeSubSchema, RemoveInvalidAdditionalItems } from "./utils"; | ||
export declare type ParseOneOfSchema<S> = Union<RecurseOnOneOfSchema<Get<S, "oneOf">, S>>; | ||
declare type RecurseOnOneOfSchema<S, P, R = never> = { | ||
stop: R; | ||
continue: S extends any[] ? RecurseOnOneOfSchema<Tail<S>, P, R | (HasKeyIn<P, "enum" | "const" | "type" | "anyOf"> extends true ? Intersection<ParseSchema<Omit<P, "oneOf">>, ParseSchema<Merge<Omit<P, "oneOf">, Merge<{ | ||
properties: {}; | ||
additionalProperties: true; | ||
required: []; | ||
}, RemoveInvalidAdditionalItems<Head<S>>>>>> : ParseSchema<Merge<Omit<P, "oneOf">, RemoveInvalidAdditionalItems<Head<S>>>>)> : never; | ||
continue: S extends any[] ? RecurseOnOneOfSchema<Tail<S>, P, R | (HasKeyIn<P, "enum" | "const" | "type" | "anyOf"> extends true ? Intersection<ParseSchema<Omit<P, "oneOf">>, ParseSchema<MergeSubSchema<Omit<P, "oneOf">, Head<S>>>> : ParseSchema<Merge<Omit<P, "oneOf">, RemoveInvalidAdditionalItems<Head<S>>>>)> : never; | ||
}[S extends [any, ...any[]] ? "continue" : "stop"]; | ||
export {}; |
@@ -5,1 +5,6 @@ import { Merge } from "../utils"; | ||
}> : Omit<S, "additionalItems">; | ||
export declare type MergeSubSchema<P, C> = Merge<P, Merge<{ | ||
properties: {}; | ||
additionalProperties: true; | ||
required: []; | ||
}, RemoveInvalidAdditionalItems<C>>>; |
@@ -9,3 +9,5 @@ export { And } from "./and"; | ||
export { DeepMergeSafe, DeepMergeUnsafe, Merge } from "./merge"; | ||
export { Not } from "./not"; | ||
export { OptionalProps } from "./optionalProps"; | ||
export { Or } from "./or"; | ||
export { Prepend } from "./prepend"; | ||
@@ -12,0 +14,0 @@ export { Prettify } from "./prettify"; |
{ | ||
"name": "json-schema-to-ts", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "Infer typescript types from your JSON schemas!", | ||
"main": "lib/index.d.ts", | ||
"scripts": { | ||
"build": "tsc --emitDeclarationOnly --declaration --declarationDir lib ./src/*.ts", | ||
"build": "rm -rf lib && tsc --emitDeclarationOnly --declaration --declarationDir lib ./src/*.ts", | ||
"test": "tsc --noEmit && jest --verbose" | ||
}, | ||
"dependencies": { | ||
"@types/json-schema": "^7.0.6" | ||
"@types/json-schema": "^7.0.6", | ||
"ts-toolbelt": "^6.15.5" | ||
}, | ||
@@ -13,0 +14,0 @@ "devDependencies": { |
113
README.md
@@ -134,3 +134,5 @@ <img src="assets/header-round-medium.png" width="100%" align="center" /> | ||
- [OneOf](#oneof) | ||
- [Not and If-Then-Else](#not-and-if-then-else) | ||
- [Not](#not) | ||
- [If/Then/Else](#ifthenelse) | ||
- [Definitions](#definitions) | ||
@@ -294,3 +296,3 @@ ## Installation | ||
- `additionalProperties` can be used to deny additional items. | ||
- `additionalProperties` can be used to deny additional properties. | ||
@@ -384,3 +386,3 @@ ```typescript | ||
Because TypeScript misses [refinment types](https://en.wikipedia.org/wiki/Refinement_type), `FromSchema` will use the `oneOf` keyword in the same way as `anyOf`: | ||
For the moment, `FromSchema` will use the `oneOf` keyword in the same way as `anyOf`: | ||
@@ -414,6 +416,8 @@ ```typescript | ||
// => FromSchema will not detect the following invalid obj 😱 | ||
// => Error will NOT be raised 😱 | ||
const invalidCat: Cat = { name: "Garfield" }; | ||
``` | ||
> This may be revised soon now that `not` exclusions are now possible | ||
### AllOf | ||
@@ -478,5 +482,64 @@ | ||
Keep in mind that the `not` type computation uses the `Exclude` utility type, which suffers from Typescript's limitations: | ||
In objects and tuples, the exclusion will propagate to properties/items if it can collapse on a single one. | ||
```typescript | ||
// 👍 Can be propagated on "animal" property | ||
const petSchema = { | ||
type: "object", | ||
properties: { | ||
animal: { enum: ["cat", "dog", "boat"] }, | ||
}, | ||
not: { | ||
properties: { animal: { const: "boat" } }, | ||
}, | ||
required: ["animal"], | ||
additionalProperties: false, | ||
} as const; | ||
type Pet = FromSchema<typeof petSchema>; | ||
// => { animal: "cat" | "dog" } | ||
``` | ||
```typescript | ||
// ❌ Cannot be propagated | ||
const petSchema = { | ||
type: "object", | ||
properties: { | ||
animal: { enum: ["cat", "dog"] }, | ||
color: { enum: ["black", "brown", "white"] }, | ||
}, | ||
not: { | ||
const: { animal: "cat", color: "white" }, | ||
}, | ||
required: ["animal", "color"], | ||
additionalProperties: false, | ||
} as const; | ||
type Pet = FromSchema<typeof petSchema>; | ||
// => { animal: "cat" | "dog", color: "black" | "brown" | "white" } | ||
``` | ||
As some actionable keywords are not yet parsed, exclusions that resolve to `never` are granted the benefit of the doubt and omitted. For the moment, `FromSchema` assumes that you are not crafting unvalidatable exclusions. | ||
```typescript | ||
const oddNumberSchema = { | ||
type: "number", | ||
not: { multipleOf: 2 }, | ||
} as const; | ||
type OddNumber = FromSchema<typeof oddNumberSchema>; | ||
// => should and will resolve to "number" | ||
const incorrectSchema = { | ||
type: "number", | ||
not: { bogus: "option" }, | ||
} as const; | ||
type Incorrect = FromSchema<typeof incorrectSchema>; | ||
// => should resolve to "never" but will still resolve to "number" | ||
``` | ||
Also, keep in mind that TypeScript misses [refinment types](https://en.wikipedia.org/wiki/Refinement_type): | ||
```typescript | ||
const goodLanguageSchema = { | ||
@@ -493,4 +556,40 @@ type: "string", | ||
### If-Then-Else | ||
### If/Then/Else | ||
The `if/then/else` keyword is not implemented yet. | ||
```typescript | ||
const petSchema = { | ||
type: "object", | ||
properties: { | ||
animal: { enum: ["cat", "dog"] }, | ||
dogBreed: { enum: Object.values(DogBreed) }, | ||
catBreed: { enum: Object.values(CatBreed) }, | ||
}, | ||
required: ["animal"], | ||
additionalProperties: false, | ||
if: { | ||
properties: { | ||
animal: { const: "dog" }, | ||
}, | ||
}, | ||
then: { | ||
required: ["dogBreed"], | ||
not: { required: ["catBreed"] }, | ||
}, | ||
else: { | ||
required: ["catBreed"], | ||
not: { required: ["dogBreed"] }, | ||
}, | ||
} as const; | ||
type Pet = FromSchema<typeof petSchema>; | ||
// => { animal: "dog"; dogBreed: DogBreed } | ||
// | { animal: "cat"; catBreed: CatBreed } | ||
``` | ||
> `FromSchema` computes the resulting type as `(If ∩ Then) ∪ (¬If ∩ Else)`. While correct in theory, remember that the `not` keyword is not perfectly assimilated, which may become an issue in some complex schemas. | ||
## Definitions | ||
Since the introduction of [template literal types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) with Typescript 4.1, the `definitions` keyword seems implementable in `json-schema-to-ts`. | ||
I'll soon be looking into it. Meanwhile, feel free to [open an issue](https://github.com/ThomasAribart/json-schema-to-ts/issues) 🤗 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
79167
72
1271
590
2
+ Addedts-toolbelt@^6.15.5
+ Addedts-toolbelt@6.15.5(transitive)