json-schema-to-ts
Advanced tools
Comparing version 0.1.10 to 1.0.0
{ | ||
"name": "json-schema-to-ts", | ||
"version": "0.1.10", | ||
"version": "1.0.0", | ||
"description": "Infer typescript types from your JSON schemas!", | ||
"main": "src/index.ts", | ||
"scripts": { | ||
"test": "jest" | ||
"test": "tsc --noEmit && jest --verbose" | ||
}, | ||
@@ -9,0 +9,0 @@ "devDependencies": { |
176
README.md
@@ -49,3 +49,4 @@ # Stop typing twice 🙅♂️ | ||
type Dog = FromSchema<typeof dogSchema>; // => Will infer the same type as above | ||
type Dog = FromSchema<typeof dogSchema>; | ||
// => Will infer the same type as above | ||
``` | ||
@@ -61,3 +62,3 @@ | ||
age: { type: "integer" }, | ||
favoriteThings: { enum: ["playing", "sleeping", "sleepingMore"] }, | ||
favoriteThings: { enum: ["playing", "sleeping", "moreSleeping"] }, | ||
}, | ||
@@ -141,4 +142,2 @@ required: ["name", "age"], | ||
You can also specify several types: | ||
```typescript | ||
@@ -153,61 +152,4 @@ const litteralsSchema = { | ||
For `object` and `array` types, properties like `required` or `additionalItems` will also work 🙌 | ||
(For `object` and `array` types, properties like `required` or `additionalItems` will work 🙌) | ||
### Objects | ||
```typescript | ||
const objectSchema = { | ||
type: "object", | ||
properties: { | ||
foo: { type: "string" }, | ||
bar: { type: "number" }, | ||
}, | ||
required: ["foo"], | ||
} as const; | ||
type Object = FromSchema<typeof objectSchema>; | ||
// => { foo: string, bar?: number } | ||
``` | ||
`FromSchema` partially supports the use of the `additionalProperties` and `patternProperties` keyword: | ||
- Contrary to the specifications, `additionalProperties` is considered `false` by default for clearer typings. Set its value to `true` to signal that additional properties can be used: | ||
```typescript | ||
const additionalPropertiesSchema = { | ||
...objectSchema, | ||
additionalProperties: true, | ||
} as const; | ||
type Object = FromSchema<typeof additionalPropertiesSchema>; | ||
// => { [x: string]: any; foo: string; bar?: number } | ||
``` | ||
- Used on their own, typed `additionalProperties` and/or `patternProperties` are supported: | ||
```typescript | ||
const typedValuesSchema = { | ||
type: "object", | ||
additionalProperties: { | ||
type: "boolean", | ||
}, | ||
} as const; | ||
type Object = FromSchema<typeof typedValuesSchema>; | ||
// => { [key: string]: boolean } | ||
const patternSchema = { | ||
type: "object", | ||
patternProperties: { | ||
"^S": { type: "string" }, | ||
"^I": { type: "integer" }, | ||
}, | ||
} as const; | ||
type Object = FromSchema<typeof patternSchema>; | ||
// => { [key: string]: string | number } | ||
``` | ||
- However, due to [TypeScript limitations](https://github.com/Microsoft/TypeScript/issues/7599), when used in combination with the `properties` keyword, extra properties will always be typed as `any` to avoid conflicts with base properties. | ||
### Arrays | ||
@@ -234,9 +176,7 @@ | ||
type Tuple = FromSchema<typeof tupleSchema>; | ||
// => [] | [boolean] | [boolean, string] | [boolean, string, ...any[]] | ||
// => [] | [boolean] | [boolean, string] | [boolean, string, ...unknown[]] | ||
``` | ||
`FromSchema` supports the `additionalItems` keyword: | ||
`FromSchema` supports the `additionalItems` specifications: | ||
- You can deny additional items: | ||
```typescript | ||
@@ -253,4 +193,2 @@ const tupleSchema = { | ||
- Or specify a type for additional items: | ||
```typescript | ||
@@ -267,3 +205,3 @@ const tupleSchema = { | ||
`FromSchema` also supports the `minItems` and `maxItems` keyword: | ||
...as well as the `minItems` and `maxItems` specifications: | ||
@@ -282,2 +220,54 @@ ```typescript | ||
(NOTE: Additional items will only work if Typescript's `strictNullChecks` option is activated) | ||
### Objects | ||
```typescript | ||
const objectSchema = { | ||
type: "object", | ||
properties: { | ||
foo: { type: "string" }, | ||
bar: { type: "number" }, | ||
}, | ||
required: ["foo"], | ||
} as const; | ||
type Object = FromSchema<typeof objectSchema>; | ||
// => { [x: string]: unknown; foo: string; bar?: number; } | ||
``` | ||
`FromSchema` partially supports the `additionalProperties` and `patternProperties` specifications: | ||
- `additionalProperties` can be used to deny additional items. | ||
```typescript | ||
const closedObjectSchema = { | ||
...objectSchema, | ||
additionalProperties: false, | ||
} as const; | ||
type Object = FromSchema<typeof additionalPropertiesSchema>; | ||
// => { foo: string; bar?: number; } | ||
``` | ||
- Used on their own, `additionalProperties` and/or `patternProperties` can be used to type unnamed properties. | ||
```typescript | ||
const objectSchema = { | ||
type: "object", | ||
additionalProperties: { | ||
type: "boolean", | ||
}, | ||
patternProperties: { | ||
"^S": { type: "string" }, | ||
"^I": { type: "integer" }, | ||
}, | ||
} as const; | ||
type Object = FromSchema<typeof typedValuesSchema>; | ||
// => { [x: string]: string | number | boolean } | ||
``` | ||
- However, when used in combination with the `properties` keyword, extra properties will always be typed as `unknown` to avoid conflicts. | ||
### AnyOf | ||
@@ -299,1 +289,49 @@ | ||
``` | ||
`FromSchema` will correctly infer factored schemas: | ||
```typescript | ||
const factoredSchema = { | ||
type: "object", | ||
properties: { | ||
bool: { type: "boolean" }, | ||
}, | ||
required: ["bool"], | ||
anyOf: [ | ||
{ | ||
properties: { | ||
str: { type: "string" }, | ||
}, | ||
required: ["str"], | ||
}, | ||
{ | ||
properties: { | ||
num: { type: "number" }, | ||
}, | ||
}, | ||
], | ||
} as const; | ||
type Factored = FromSchema<typeof factoredSchema>; | ||
// => { | ||
// [x:string]: unknown; | ||
// bool: boolean; | ||
// str: string; | ||
// } | { | ||
// [x:string]: unknown; | ||
// bool: boolean; | ||
// num?: number; | ||
// } | ||
``` | ||
### OneOf | ||
...Coming soon 😃 | ||
### AllOf | ||
...Coming soon 😃 | ||
### If/Else | ||
...Coming soon 😃 |
@@ -1,7 +0,3 @@ | ||
import { FromAnyOfSchema } from "./anyOf"; | ||
import { FromEnumSchema } from "./enum"; | ||
import { FromConstSchema } from "./const"; | ||
import { FromMixedSchema } from "./mixed"; | ||
import { FromObjectSchema } from "./object"; | ||
import { FromArraySchema } from "./array"; | ||
import { Resolve } from "./meta-types"; | ||
import { ParseSchema } from "./parse-schema"; | ||
import { Writeable } from "./utils"; | ||
@@ -12,52 +8,4 @@ | ||
* | ||
* Args: | ||
* - `Schema`: JSON schema | ||
* @param S JSON schema | ||
*/ | ||
export type FromSchema<S> = FromReadonlySchema<S>; | ||
type FromReadonlySchema<S> = FromWriteableSchema<Writeable<S>>; | ||
export type FromWriteableSchema<S> = { | ||
any: any; | ||
never: never; | ||
anyOf: FromAnyOfSchema<S>; | ||
enum: FromEnumSchema<S>; | ||
const: FromConstSchema<S>; | ||
mixed: FromMixedSchema<S>; | ||
null: null; | ||
boolean: boolean; | ||
number: number; | ||
string: string; | ||
object: FromObjectSchema<S>; | ||
array: FromArraySchema<S>; | ||
structureError: "TypeError: Invalid schema structure"; | ||
typeError: "TypeError: Invalid type value. Did you forget to use the 'as const' directive?"; | ||
}[InferSchemaType<S>]; | ||
type InferSchemaType<S> = S extends true | string | ||
? "any" | ||
: S extends false | ||
? "never" | ||
: "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" | ||
: "typeError" | ||
: "structureError"; | ||
export type FromSchema<S> = Resolve<ParseSchema<Writeable<S>>>; |
@@ -1,2 +0,2 @@ | ||
import { MergeRight } from "./utils"; | ||
import { UnsafeMergeRec } from "./utils"; | ||
@@ -13,13 +13,16 @@ type CommonProps = { | ||
type MakeSchema<SpecificProps = {}> = MergeRight<CommonProps, SpecificProps>; | ||
type MakeSchema<SpecificProps = {}> = UnsafeMergeRec< | ||
CommonProps, | ||
SpecificProps | ||
>; | ||
export type NullSchema = MakeSchema<{ | ||
type NullSchema = MakeSchema<{ | ||
type: "null"; | ||
}>; | ||
export type BooleanSchema = MakeSchema<{ | ||
type BooleanSchema = MakeSchema<{ | ||
type: "boolean"; | ||
}>; | ||
export type StringSchema = MakeSchema<{ | ||
type StringSchema = MakeSchema<{ | ||
type: "string"; | ||
@@ -32,3 +35,3 @@ maxLength?: number; | ||
export type IntegerSchema = MakeSchema<{ | ||
type IntegerSchema = MakeSchema<{ | ||
type: "integer"; | ||
@@ -42,3 +45,3 @@ multipleOf?: number; | ||
export type NumberSchema = MakeSchema<{ | ||
type NumberSchema = MakeSchema<{ | ||
type: "number"; | ||
@@ -64,3 +67,3 @@ multipleOf?: number; | ||
export type ArraySchema = MakeSchema<{ | ||
type ArraySchema = MakeSchema<{ | ||
type: "array"; | ||
@@ -75,7 +78,7 @@ items?: Schema | any[]; | ||
export type AnyOfSchema = MakeSchema<{ | ||
type AnyOfSchema = MakeSchema<{ | ||
anyOf: any[]; | ||
}>; | ||
export type EnumSchema = MakeSchema<{ | ||
type ConstSchema = MakeSchema<{ | ||
type?: | ||
@@ -89,6 +92,6 @@ | "null" | ||
| "array"; | ||
enum: any[]; | ||
const: any; | ||
}>; | ||
export type ConstSchema = MakeSchema<{ | ||
type EnumSchema = MakeSchema<{ | ||
type?: | ||
@@ -102,3 +105,3 @@ | "null" | ||
| "array"; | ||
const: any; | ||
enum: any[]; | ||
}>; | ||
@@ -105,0 +108,0 @@ |
import { Head } from "./head"; | ||
import { Tail } from "./tail"; | ||
import { Prepend } from "./prepend"; | ||
import { Reverse } from "./reverse"; | ||
@@ -8,11 +9,20 @@ /** | ||
* | ||
* Args: | ||
* - `A`: Tuple `A` | ||
* - `B`: Tuple `B` | ||
* @param A Tuple | ||
* @param B Tuple | ||
* @return Tuple | ||
*/ | ||
export type ConcatReversed<A, B extends any[]> = { | ||
stop: B; | ||
continue: A extends any[] | ||
? ConcatReversed<Tail<A>, Prepend<Head<A>, B>> | ||
: never; | ||
continue: ConcatReversed<Tail<A>, Prepend<Head<A>, B>>; | ||
}[A extends [any, ...any[]] ? "continue" : "stop"]; | ||
/** | ||
* Concatenate tuple `A` to tuple `B` | ||
* | ||
* @param A Tuple | ||
* @param B Tuple | ||
* @return Tuple | ||
*/ | ||
export type Concat<A, B extends any[]> = A extends any[] | ||
? ConcatReversed<Reverse<A>, B> | ||
: never; |
/** | ||
* Returns `true` if type `A` extends type `B`, `false` if not | ||
* | ||
* Args: | ||
* - `A`: Type `A` | ||
* - `B`: Type `B` | ||
* @param A Type | ||
* @param B Type | ||
* @return Boolean | ||
*/ | ||
export type DoesExtend<A, B> = A extends B ? true : false; | ||
type ArrayKeys = keyof []; | ||
/** | ||
* Returns `true` if both types extend each other, `false` if not | ||
* Returns `true` if type is object, `false` if not (excludes arrays) | ||
* | ||
* Args: | ||
* - `A`: Type `A` | ||
* - `B`: Type `B` | ||
* @param T Type | ||
* @return Boolean | ||
*/ | ||
export type DoesBothExtend<A, B> = DoesExtend<A, B> extends true | ||
? DoesExtend<B, A> | ||
export type IsObject<T> = T extends object | ||
? ArrayKeys extends Extract<keyof T, ArrayKeys> | ||
? false | ||
: true | ||
: false; | ||
/** | ||
* Returns `true` if type is array, `false` if not (excludes objects) | ||
* | ||
* @param T Type | ||
* @return Boolean | ||
*/ | ||
export type IsArray<T> = T extends object | ||
? ArrayKeys extends Extract<keyof T, ArrayKeys> | ||
? true | ||
: false | ||
: false; |
@@ -0,8 +1,17 @@ | ||
import { Head } from "./head"; | ||
import { Tail } from "./tail"; | ||
/** | ||
* Returns the property of an object, `never` if property misses from object | ||
* Returns the property value of an object `O`, `F` if property key misses from object | ||
* | ||
* Args: | ||
* - `Object`: Object | ||
* - `Property`: Property name | ||
* @param O Object | ||
* @param K Property key | ||
* @param F _(optional:_ `never` _)_ Fallback type | ||
* @return Type | ||
*/ | ||
export type Get<O, P extends string> = P extends keyof O ? O[P] : never; | ||
export type Get<O, K, F = never> = K extends keyof O ? O[K] : F; | ||
export type GetRec<O, K> = { | ||
continue: GetRec<Get<O, Head<K>>, Tail<K>>; | ||
stop: O; | ||
}[K extends [any, ...any[]] ? "continue" : "stop"]; |
/** | ||
* Returns the first element of a tuple | ||
* Returns the first element of a tuple `T` | ||
* | ||
* Args: | ||
* - `Tuple`: Tuple | ||
* @param T Tuple | ||
* @return Type | ||
*/ | ||
export type Head<T extends any[]> = T[0]; | ||
export type Head<T> = T extends any[] ? T[0] : never; |
@@ -1,9 +0,12 @@ | ||
export { Get } from "./get"; | ||
export { And } from "./and"; | ||
export { ConcatReversed, Concat } from "./concat"; | ||
export { DoesExtend, IsObject, IsArray } from "./extends"; | ||
export { FilterExtending } from "./filter"; | ||
export { Get, GetRec } from "./get"; | ||
export { HasKeyIn } from "./hasKeyIn"; | ||
export { Head } from "./head"; | ||
export { Tail } from "./tail"; | ||
export { SafeMergeRec, UnsafeMergeRec, Merge } from "./merge"; | ||
export { Prepend } from "./prepend"; | ||
export { Reverse } from "./reverse"; | ||
export { ConcatReversed } from "./concat"; | ||
export { MergeRight } from "./merge"; | ||
export { Tail } from "./tail"; | ||
export { Writeable } from "./writeable"; | ||
export { DoesExtend, DoesBothExtend } from "./extends"; |
@@ -0,16 +1,22 @@ | ||
import { Concat } from "./concat"; | ||
import { IsObject, IsArray } from "./extends"; | ||
/** | ||
* Merge two types `A` and `B`: | ||
* - Returns `B` if `A` or `B` is not an object | ||
* Recursively merge two types `A` and `B`: | ||
* - Returns `B` if `A` and `B` are not both objects or arrays | ||
* - Recursively merge `A` and `B` properties if both are objects | ||
* - Concat `A` and `B` if both are arrays | ||
* | ||
* Args: | ||
* - `A`: Type `A` | ||
* - `B`: Type `B` | ||
* `UnsafeMergeRec` preserves non-required properties, but can return `never` if TS infers that `A & B = never` (which can happen if some properties are incompatible) | ||
* | ||
* @param A Type | ||
* @param B Type | ||
* @return Type | ||
*/ | ||
export type MergeRight<A, B> = A extends object | ||
? B extends object | ||
export type UnsafeMergeRec<A, B> = IsObject<A> extends true | ||
? IsObject<B> extends true | ||
? { | ||
[K in keyof (A & B)]: K extends keyof B | ||
? K extends keyof A | ||
? MergeRight<A[K], B[K]> | ||
? UnsafeMergeRec<A[K], B[K]> | ||
: B[K] | ||
@@ -22,2 +28,62 @@ : K extends keyof A | ||
: B | ||
: IsArray<A> extends true | ||
? IsArray<B> extends true | ||
? B extends any[] | ||
? Concat<A, B> | ||
: never | ||
: B | ||
: B; | ||
/** | ||
* Recursively merge two types `A` and `B`: | ||
* - Returns `B` if `A` and `B` are not both objects or arrays | ||
* - Recursively merge `A` and `B` properties if both are objects | ||
* - Concat `A` and `B` if both are arrays | ||
* | ||
* Contrary to `UnsafeMergeRec`, `SafeMergeRec` never returns never, but doesn't preserve non-required properties | ||
* | ||
* @param A Type | ||
* @param B Type | ||
* @return Type | ||
*/ | ||
export type SafeMergeRec<A, B> = IsObject<A> extends true | ||
? IsObject<B> extends true | ||
? { | ||
[K in keyof A | keyof B]: K extends keyof B | ||
? K extends keyof A | ||
? SafeMergeRec<A[K], B[K]> | ||
: B[K] | ||
: K extends keyof A | ||
? A[K] | ||
: never; | ||
} | ||
: B | ||
: IsArray<A> extends true | ||
? IsArray<B> extends true | ||
? B extends any[] | ||
? Concat<A, B> | ||
: never | ||
: B | ||
: B; | ||
/** | ||
* Merge two types `A` and `B`: | ||
* - Returns `B` if `A` and `B` are not both objects | ||
* - Merge `A` and `B` properties if both are objects | ||
* - Merging is not recursive: Properties of `B` erase properties of `A` | ||
* | ||
* @param A Type | ||
* @param B Type | ||
* @return Type | ||
*/ | ||
export type Merge<A, B> = IsObject<A> extends true | ||
? IsObject<B> extends true | ||
? { | ||
[K in keyof A | keyof B]: K extends keyof B | ||
? B[K] | ||
: K extends keyof A | ||
? A[K] | ||
: never; | ||
} | ||
: B | ||
: B; |
/** | ||
* Inserts an element at the start of a tuple | ||
* | ||
* Args: | ||
* - `Element`: Element to insert | ||
* - `Tuple`: Tuple | ||
* @param E Element (type) | ||
* @param T Tuple | ||
* @return Tuple | ||
*/ | ||
@@ -8,0 +8,0 @@ export type Prepend<E, T extends any[]> = (( |
@@ -6,11 +6,10 @@ import { Head } from "./head"; | ||
/** | ||
* Reverses a tuple | ||
* Reverses a tuple `T` | ||
* | ||
* Args: | ||
* - `Tuple`: Tuple | ||
* - `Result`: _(optional)_ Reversed tuple | ||
* @param T Tuple | ||
* @return Tuple | ||
*/ | ||
export type Reverse<T extends any[], R extends any[] = []> = { | ||
export type Reverse<T, R extends any[] = []> = { | ||
stop: R; | ||
continue: Reverse<Tail<T>, Prepend<Head<T>, R>>; | ||
continue: T extends any[] ? Reverse<Tail<T>, Prepend<Head<T>, R>> : never; | ||
}[T extends [any, ...any[]] ? "continue" : "stop"]; |
/** | ||
* Remove the first element of a tuple | ||
* | ||
* Args: | ||
* - `Tuple`: Tuple | ||
* @param T Tuple | ||
* @return Tuple | ||
*/ | ||
export type Tail<T extends any[]> = ((...args: T) => void) extends ( | ||
head: any, | ||
...tail: infer R | ||
) => void | ||
? R | ||
: T; | ||
export type Tail<T> = T extends any[] | ||
? ((...args: T) => void) extends (head: any, ...tail: infer R) => void | ||
? R | ||
: T | ||
: never; |
/** | ||
* Recursively remove the `readonly` directive from an object properties | ||
* | ||
* Args: | ||
* - `Object`: Object | ||
* @param O Object | ||
* @return Object | ||
*/ | ||
@@ -7,0 +7,0 @@ export type Writeable<O> = O extends object |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
43823
44
1196
0
329
1