.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / table / node_modules / ajv / lib / types / jtd-schema.ts
1 /** required keys of an object, not undefined */
2 type RequiredKeys<T> = {
3   [K in keyof T]-?: undefined extends T[K] ? never : K
4 }[keyof T]
5
6 /** optional or undifined-able keys of an object */
7 type OptionalKeys<T> = {
8   [K in keyof T]-?: undefined extends T[K] ? K : never
9 }[keyof T]
10
11 /** type is true if T is a union type */
12 type IsUnion_<T, U extends T = T> = false extends (
13   T extends unknown ? ([U] extends [T] ? false : true) : never
14 )
15   ? false
16   : true
17 type IsUnion<T> = IsUnion_<T>
18
19 /** type is true if T is identically E */
20 type TypeEquality<T, E> = [T] extends [E] ? ([E] extends [T] ? true : false) : false
21
22 /** type is true if T or null is identically E or null*/
23 type NullTypeEquality<T, E> = TypeEquality<T | null, E | null>
24
25 /** gets only the string literals of a type or null if a type isn't a string literal */
26 type EnumString<T> = [T] extends [never]
27   ? null
28   : T extends string
29   ? string extends T
30     ? null
31     : T
32   : null
33
34 /** true if type is a union of string literals */
35 type IsEnum<T> = null extends EnumString<Exclude<T, null>> ? false : true
36
37 /** true only if all types are array types (not tuples) */
38 // NOTE relies on the fact that tuples don't have an index at 0.5, but arrays
39 // have an index at every number
40 type IsElements<T> = false extends IsUnion<T>
41   ? [T] extends [readonly unknown[]]
42     ? undefined extends T[0.5]
43       ? false
44       : true
45     : false
46   : false
47
48 /** true if the the type is a values type */
49 type IsValues<T> = false extends IsUnion<Exclude<T, null>>
50   ? TypeEquality<keyof Exclude<T, null>, string>
51   : false
52
53 /** true if type is a proeprties type and Union is false, or type is a discriminator type and Union is true */
54 type IsRecord<T, Union extends boolean> = Union extends IsUnion<Exclude<T, null>>
55   ? null extends EnumString<keyof Exclude<T, null>>
56     ? false
57     : true
58   : false
59
60 /** numeric strings */
61 type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"
62
63 /** string strings */
64 type StringType = "string" | "timestamp"
65
66 /** actual schema */
67 export type JTDSchemaType<T, D extends Record<string, unknown> = Record<string, never>> = (
68   | // refs - where null wasn't specified, must match exactly
69   (null extends EnumString<keyof D>
70       ? never
71       :
72           | ({[K in keyof D]: [T] extends [D[K]] ? {ref: K} : never}[keyof D] & {nullable?: false})
73           // nulled refs - if ref is nullable and nullable is specified, then it can
74           // match either null or non-null definitions
75           | (null extends T
76               ? {
77                   [K in keyof D]: [Exclude<T, null>] extends [Exclude<D[K], null>]
78                     ? {ref: K}
79                     : never
80                 }[keyof D] & {nullable: true}
81               : never))
82   // empty - empty schemas also treat nullable differently in that it's now fully ignored
83   | (unknown extends T ? {nullable?: boolean} : never)
84   // all other types
85   | ((// numbers - only accepts the type number
86     true extends NullTypeEquality<T, number>
87       ? {type: NumberType}
88       : // booleans - accepts the type boolean
89       true extends NullTypeEquality<T, boolean>
90       ? {type: "boolean"}
91       : // strings - only accepts the type string
92       true extends NullTypeEquality<T, string>
93       ? {type: StringType}
94       : // strings - only accepts the type Date
95       true extends NullTypeEquality<T, Date>
96       ? {type: "timestamp"}
97       : // enums - only accepts union of string literals
98       // TODO we can't actually check that everything in the union was specified
99       true extends IsEnum<T>
100       ? {enum: EnumString<Exclude<T, null>>[]}
101       : // arrays - only accepts arrays, could be array of unions to be resolved later
102       true extends IsElements<Exclude<T, null>>
103       ? T extends readonly (infer E)[]
104         ? {
105             elements: JTDSchemaType<E, D>
106           }
107         : never
108       : // values
109       true extends IsValues<T>
110       ? T extends Record<string, infer V>
111         ? {
112             values: JTDSchemaType<V>
113           }
114         : never
115       : // properties
116       true extends IsRecord<T, false>
117       ? ([RequiredKeys<Exclude<T, null>>] extends [never]
118           ? {
119               properties?: Record<string, never>
120             }
121           : {
122               properties: {[K in RequiredKeys<T>]: JTDSchemaType<T[K], D>}
123             }) &
124           ([OptionalKeys<Exclude<T, null>>] extends [never]
125             ? {
126                 optionalProperties?: Record<string, never>
127               }
128             : {
129                 optionalProperties: {
130                   [K in OptionalKeys<T>]: JTDSchemaType<Exclude<T[K], undefined>, D>
131                 }
132               }) & {
133             additionalProperties?: boolean
134           }
135       : // discriminator
136       true extends IsRecord<T, true>
137       ? {
138           [K in keyof Exclude<T, null>]-?: Exclude<T, null>[K] extends string
139             ? {
140                 discriminator: K
141                 mapping: {
142                   // TODO currently allows descriminator to be present in schema
143                   [M in Exclude<T, null>[K]]: JTDSchemaType<
144                     Omit<T extends {[C in K]: M} ? T : never, K>,
145                     D
146                   >
147                 }
148               }
149             : never
150         }[keyof Exclude<T, null>]
151       : never) &
152       (null extends T
153         ? {
154             nullable: true
155           }
156         : {nullable?: false}))
157 ) & {
158   // extra properties
159   metadata?: Record<string, unknown>
160   // TODO these should only be allowed at the top level
161   definitions?: {[K in keyof D]: JTDSchemaType<D[K], D>}
162 }
163
164 type JTDDataDef<S, D extends Record<string, unknown>> =
165   | (// ref
166     S extends {ref: string}
167       ? JTDDataDef<D[S["ref"]], D>
168       : // type
169       S extends {type: NumberType}
170       ? number
171       : S extends {type: "string"}
172       ? string
173       : S extends {type: "timestamp"}
174       ? string | Date
175       : // enum
176       S extends {enum: readonly (infer E)[]}
177       ? string extends E
178         ? never
179         : [E] extends [string]
180         ? E
181         : never
182       : // elements
183       S extends {elements: infer E}
184       ? JTDDataDef<E, D>[]
185       : // properties
186       S extends {
187           properties: Record<string, unknown>
188           optionalProperties?: Record<string, unknown>
189           additionalProperties?: boolean
190         }
191       ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} &
192           {
193             -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
194               S["optionalProperties"][K],
195               D
196             >
197           } &
198           ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
199       : S extends {
200           properties?: Record<string, unknown>
201           optionalProperties: Record<string, unknown>
202           additionalProperties?: boolean
203         }
204       ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} &
205           {
206             -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
207               S["optionalProperties"][K],
208               D
209             >
210           } &
211           ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
212       : // values
213       S extends {values: infer V}
214       ? Record<string, JTDDataDef<V, D>>
215       : // discriminator
216       S extends {discriminator: infer M; mapping: Record<string, unknown>}
217       ? [M] extends [string]
218         ? {
219             [K in keyof S["mapping"]]: JTDDataDef<S["mapping"][K], D> & {[KM in M]: K}
220           }[keyof S["mapping"]]
221         : never
222       : // empty
223         unknown)
224   | (S extends {nullable: true} ? null : never)
225
226 export type JTDDataType<S> = S extends {definitions: Record<string, unknown>}
227   ? JTDDataDef<S, S["definitions"]>
228   : JTDDataDef<S, Record<string, never>>