UNPKG

6.37 kBPlain TextView Raw
1/* eslint-disable @typescript-eslint/no-empty-interface */
2type StrictNullChecksWrapper<Name extends string, Type> = undefined extends null
3 ? `strictNullChecks must be true in tsconfig to use ${Name}`
4 : Type
5
6type UnionToIntersection<U> = (U extends any ? (_: U) => void : never) extends (_: infer I) => void
7 ? I
8 : never
9
10export type SomeJSONSchema = UncheckedJSONSchemaType<Known, true>
11
12type UncheckedPartialSchema<T> = Partial<UncheckedJSONSchemaType<T, true>>
13
14export type PartialSchema<T> = StrictNullChecksWrapper<"PartialSchema", UncheckedPartialSchema<T>>
15
16type JSONType<T extends string, IsPartial extends boolean> = IsPartial extends true
17 ? T | undefined
18 : T
19
20interface NumberKeywords {
21 minimum?: number
22 maximum?: number
23 exclusiveMinimum?: number
24 exclusiveMaximum?: number
25 multipleOf?: number
26 format?: string
27}
28
29interface StringKeywords {
30 minLength?: number
31 maxLength?: number
32 pattern?: string
33 format?: string
34}
35
36type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
37 | // these two unions allow arbitrary unions of types
38 {
39 anyOf: readonly UncheckedJSONSchemaType<T, IsPartial>[]
40 }
41 | {
42 oneOf: readonly UncheckedJSONSchemaType<T, IsPartial>[]
43 }
44 // this union allows for { type: (primitive)[] } style schemas
45 | ({
46 type: readonly (T extends number
47 ? JSONType<"number" | "integer", IsPartial>
48 : T extends string
49 ? JSONType<"string", IsPartial>
50 : T extends boolean
51 ? JSONType<"boolean", IsPartial>
52 : never)[]
53 } & UnionToIntersection<
54 T extends number
55 ? NumberKeywords
56 : T extends string
57 ? StringKeywords
58 : T extends boolean
59 ? // eslint-disable-next-line @typescript-eslint/ban-types
60 {}
61 : never
62 >)
63 // this covers "normal" types; it's last so typescript looks to it first for errors
64 | ((T extends number
65 ? {
66 type: JSONType<"number" | "integer", IsPartial>
67 } & NumberKeywords
68 : T extends string
69 ? {
70 type: JSONType<"string", IsPartial>
71 } & StringKeywords
72 : T extends boolean
73 ? {
74 type: JSONType<"boolean", IsPartial>
75 }
76 : T extends readonly [any, ...any[]]
77 ? {
78 // JSON AnySchema for tuple
79 type: JSONType<"array", IsPartial>
80 items: {
81 readonly [K in keyof T]-?: UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>
82 } & {length: T["length"]}
83 minItems: T["length"]
84 } & ({maxItems: T["length"]} | {additionalItems: false})
85 : T extends readonly any[]
86 ? {
87 type: JSONType<"array", IsPartial>
88 items: UncheckedJSONSchemaType<T[0], false>
89 contains?: UncheckedPartialSchema<T[0]>
90 minItems?: number
91 maxItems?: number
92 minContains?: number
93 maxContains?: number
94 uniqueItems?: true
95 additionalItems?: never
96 }
97 : T extends Record<string, any>
98 ? {
99 // JSON AnySchema for records and dictionaries
100 // "required" is not optional because it is often forgotten
101 // "properties" are optional for more concise dictionary schemas
102 // "patternProperties" and can be only used with interfaces that have string index
103 type: JSONType<"object", IsPartial>
104 additionalProperties?: boolean | UncheckedJSONSchemaType<T[string], false>
105 unevaluatedProperties?: boolean | UncheckedJSONSchemaType<T[string], false>
106 properties?: IsPartial extends true
107 ? Partial<UncheckedPropertiesSchema<T>>
108 : UncheckedPropertiesSchema<T>
109 patternProperties?: Record<string, UncheckedJSONSchemaType<T[string], false>>
110 propertyNames?: Omit<UncheckedJSONSchemaType<string, false>, "type"> & {type?: "string"}
111 dependencies?: {[K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema<T>}
112 dependentRequired?: {[K in keyof T]?: Readonly<(keyof T)[]>}
113 dependentSchemas?: {[K in keyof T]?: UncheckedPartialSchema<T>}
114 minProperties?: number
115 maxProperties?: number
116 } & (IsPartial extends true // "required" is not necessary if it's a non-partial type with no required keys // are listed it only asserts that optional cannot be listed. // "required" type does not guarantee that all required properties
117 ? {required: Readonly<(keyof T)[]>}
118 : [UncheckedRequiredMembers<T>] extends [never]
119 ? {required?: Readonly<UncheckedRequiredMembers<T>[]>}
120 : {required: Readonly<UncheckedRequiredMembers<T>[]>})
121 : T extends null
122 ? {
123 type: JSONType<"null", IsPartial>
124 nullable: true
125 }
126 : never) & {
127 allOf?: Readonly<UncheckedPartialSchema<T>[]>
128 anyOf?: Readonly<UncheckedPartialSchema<T>[]>
129 oneOf?: Readonly<UncheckedPartialSchema<T>[]>
130 if?: UncheckedPartialSchema<T>
131 then?: UncheckedPartialSchema<T>
132 else?: UncheckedPartialSchema<T>
133 not?: UncheckedPartialSchema<T>
134 })
135) & {
136 [keyword: string]: any
137 $id?: string
138 $ref?: string
139 $defs?: Record<string, UncheckedJSONSchemaType<Known, true>>
140 definitions?: Record<string, UncheckedJSONSchemaType<Known, true>>
141}
142
143export type JSONSchemaType<T> = StrictNullChecksWrapper<
144 "JSONSchemaType",
145 UncheckedJSONSchemaType<T, false>
146>
147
148type Known =
149 | {[key: string]: Known}
150 | [Known, ...Known[]]
151 | Known[]
152 | number
153 | string
154 | boolean
155 | null
156
157type UncheckedPropertiesSchema<T> = {
158 [K in keyof T]-?: (UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>) | {$ref: string}
159}
160
161export type PropertiesSchema<T> = StrictNullChecksWrapper<
162 "PropertiesSchema",
163 UncheckedPropertiesSchema<T>
164>
165
166type UncheckedRequiredMembers<T> = {
167 [K in keyof T]-?: undefined extends T[K] ? never : K
168}[keyof T]
169
170export type RequiredMembers<T> = StrictNullChecksWrapper<
171 "RequiredMembers",
172 UncheckedRequiredMembers<T>
173>
174
175type Nullable<T> = undefined extends T
176 ? {
177 nullable: true
178 const?: null // any non-null value would fail `const: null`, `null` would fail any other value in const
179 enum?: Readonly<(T | null)[]> // `null` must be explicitly included in "enum" for `null` to pass
180 default?: T | null
181 }
182 : {
183 const?: T
184 enum?: Readonly<T[]>
185 default?: T
186 }