1 |
|
2 | type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"
|
3 |
|
4 |
|
5 | type StringType = "string" | "timestamp"
|
6 |
|
7 |
|
8 | export type SomeJTDSchemaType = (
|
9 | |
|
10 | {ref: string}
|
11 |
|
12 | | {type: NumberType | StringType | "boolean"}
|
13 |
|
14 | | {enum: string[]}
|
15 |
|
16 | | {elements: SomeJTDSchemaType}
|
17 |
|
18 | | {values: SomeJTDSchemaType}
|
19 |
|
20 | | {
|
21 | properties: Record<string, SomeJTDSchemaType>
|
22 | optionalProperties?: Record<string, SomeJTDSchemaType>
|
23 | additionalProperties?: boolean
|
24 | }
|
25 | | {
|
26 | properties?: Record<string, SomeJTDSchemaType>
|
27 | optionalProperties: Record<string, SomeJTDSchemaType>
|
28 | additionalProperties?: boolean
|
29 | }
|
30 |
|
31 | | {discriminator: string; mapping: Record<string, SomeJTDSchemaType>}
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | | {}
|
37 | ) & {
|
38 | nullable?: boolean
|
39 | metadata?: Record<string, unknown>
|
40 | definitions?: Record<string, SomeJTDSchemaType>
|
41 | }
|
42 |
|
43 |
|
44 | type RequiredKeys<T> = {
|
45 | [K in keyof T]-?: undefined extends T[K] ? never : K
|
46 | }[keyof T]
|
47 |
|
48 |
|
49 | type OptionalKeys<T> = {
|
50 | [K in keyof T]-?: undefined extends T[K] ? K : never
|
51 | }[keyof T]
|
52 |
|
53 |
|
54 | type IsUnion_<T, U extends T = T> = false extends (
|
55 | T extends unknown ? ([U] extends [T] ? false : true) : never
|
56 | )
|
57 | ? false
|
58 | : true
|
59 | type IsUnion<T> = IsUnion_<T>
|
60 |
|
61 |
|
62 | type TypeEquality<T, E> = [T] extends [E] ? ([E] extends [T] ? true : false) : false
|
63 |
|
64 |
|
65 | type NullTypeEquality<T, E> = TypeEquality<T | null, E | null>
|
66 |
|
67 |
|
68 | type EnumString<T> = [T] extends [never]
|
69 | ? null
|
70 | : T extends string
|
71 | ? string extends T
|
72 | ? null
|
73 | : T
|
74 | : null
|
75 |
|
76 |
|
77 | type IsEnum<T> = null extends EnumString<Exclude<T, null>> ? false : true
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | type IsElements<T> = false extends IsUnion<T>
|
83 | ? [T] extends [readonly unknown[]]
|
84 | ? undefined extends T[0.5]
|
85 | ? false
|
86 | : true
|
87 | : false
|
88 | : false
|
89 |
|
90 |
|
91 | type IsValues<T> = false extends IsUnion<Exclude<T, null>>
|
92 | ? TypeEquality<keyof Exclude<T, null>, string>
|
93 | : false
|
94 |
|
95 |
|
96 | type IsRecord<T, Union extends boolean> = Union extends IsUnion<Exclude<T, null>>
|
97 | ? null extends EnumString<keyof Exclude<T, null>>
|
98 | ? false
|
99 | : true
|
100 | : false
|
101 |
|
102 |
|
103 | export type JTDSchemaType<T, D extends Record<string, unknown> = Record<string, never>> = (
|
104 | |
|
105 | (null extends EnumString<keyof D>
|
106 | ? never
|
107 | :
|
108 | | ({[K in keyof D]: [T] extends [D[K]] ? {ref: K} : never}[keyof D] & {nullable?: false})
|
109 |
|
110 |
|
111 | | (null extends T
|
112 | ? {
|
113 | [K in keyof D]: [Exclude<T, null>] extends [Exclude<D[K], null>]
|
114 | ? {ref: K}
|
115 | : never
|
116 | }[keyof D] & {nullable: true}
|
117 | : never))
|
118 |
|
119 | | (unknown extends T ? {nullable?: boolean} : never)
|
120 |
|
121 | | ((true extends NullTypeEquality<T, number>
|
122 | ? {type: NumberType}
|
123 | :
|
124 | true extends NullTypeEquality<T, boolean>
|
125 | ? {type: "boolean"}
|
126 | :
|
127 | true extends NullTypeEquality<T, string>
|
128 | ? {type: StringType}
|
129 | :
|
130 | true extends NullTypeEquality<T, Date>
|
131 | ? {type: "timestamp"}
|
132 | :
|
133 |
|
134 | true extends IsEnum<T>
|
135 | ? {enum: EnumString<Exclude<T, null>>[]}
|
136 | :
|
137 | true extends IsElements<Exclude<T, null>>
|
138 | ? T extends readonly (infer E)[]
|
139 | ? {
|
140 | elements: JTDSchemaType<E, D>
|
141 | }
|
142 | : never
|
143 | :
|
144 | true extends IsValues<T>
|
145 | ? T extends Record<string, infer V>
|
146 | ? {
|
147 | values: JTDSchemaType<V, D>
|
148 | }
|
149 | : never
|
150 | :
|
151 | true extends IsRecord<T, false>
|
152 | ? ([RequiredKeys<Exclude<T, null>>] extends [never]
|
153 | ? {
|
154 | properties?: Record<string, never>
|
155 | }
|
156 | : {
|
157 | properties: {[K in RequiredKeys<T>]: JTDSchemaType<T[K], D>}
|
158 | }) &
|
159 | ([OptionalKeys<Exclude<T, null>>] extends [never]
|
160 | ? {
|
161 | optionalProperties?: Record<string, never>
|
162 | }
|
163 | : {
|
164 | optionalProperties: {
|
165 | [K in OptionalKeys<T>]: JTDSchemaType<Exclude<T[K], undefined>, D>
|
166 | }
|
167 | }) & {
|
168 | additionalProperties?: boolean
|
169 | }
|
170 | :
|
171 | true extends IsRecord<T, true>
|
172 | ? {
|
173 | [K in keyof Exclude<T, null>]-?: Exclude<T, null>[K] extends string
|
174 | ? {
|
175 | discriminator: K
|
176 | mapping: {
|
177 |
|
178 | [M in Exclude<T, null>[K]]: JTDSchemaType<
|
179 | Omit<T extends {[C in K]: M} ? T : never, K>,
|
180 | D
|
181 | >
|
182 | }
|
183 | }
|
184 | : never
|
185 | }[keyof Exclude<T, null>]
|
186 | : never) &
|
187 | (null extends T
|
188 | ? {
|
189 | nullable: true
|
190 | }
|
191 | : {nullable?: false}))
|
192 | ) & {
|
193 |
|
194 | metadata?: Record<string, unknown>
|
195 |
|
196 | definitions?: {[K in keyof D]: JTDSchemaType<D[K], D>}
|
197 | }
|
198 |
|
199 | type JTDDataDef<S, D extends Record<string, unknown>> =
|
200 | |
|
201 | (S extends {ref: string}
|
202 | ? D extends {[K in S["ref"]]: infer V}
|
203 | ? JTDDataDef<V, D>
|
204 | : never
|
205 | :
|
206 | S extends {type: NumberType}
|
207 | ? number
|
208 | : S extends {type: "boolean"}
|
209 | ? boolean
|
210 | : S extends {type: "string"}
|
211 | ? string
|
212 | : S extends {type: "timestamp"}
|
213 | ? string | Date
|
214 | :
|
215 | S extends {enum: readonly (infer E)[]}
|
216 | ? string extends E
|
217 | ? never
|
218 | : [E] extends [string]
|
219 | ? E
|
220 | : never
|
221 | :
|
222 | S extends {elements: infer E}
|
223 | ? JTDDataDef<E, D>[]
|
224 | :
|
225 | S extends {
|
226 | properties: Record<string, unknown>
|
227 | optionalProperties?: Record<string, unknown>
|
228 | additionalProperties?: boolean
|
229 | }
|
230 | ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} & {
|
231 | -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
|
232 | S["optionalProperties"][K],
|
233 | D
|
234 | >
|
235 | } & ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
|
236 | : S extends {
|
237 | properties?: Record<string, unknown>
|
238 | optionalProperties: Record<string, unknown>
|
239 | additionalProperties?: boolean
|
240 | }
|
241 | ? {-readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D>} & {
|
242 | -readonly [K in keyof S["optionalProperties"]]+?: JTDDataDef<
|
243 | S["optionalProperties"][K],
|
244 | D
|
245 | >
|
246 | } & ([S["additionalProperties"]] extends [true] ? Record<string, unknown> : unknown)
|
247 | :
|
248 | S extends {values: infer V}
|
249 | ? Record<string, JTDDataDef<V, D>>
|
250 | :
|
251 | S extends {discriminator: infer M; mapping: Record<string, unknown>}
|
252 | ? [M] extends [string]
|
253 | ? {
|
254 | [K in keyof S["mapping"]]: JTDDataDef<S["mapping"][K], D> & {[KM in M]: K}
|
255 | }[keyof S["mapping"]]
|
256 | : never
|
257 | :
|
258 | unknown)
|
259 | | (S extends {nullable: true} ? null : never)
|
260 |
|
261 | export type JTDDataType<S> = S extends {definitions: Record<string, unknown>}
|
262 | ? JTDDataDef<S, S["definitions"]>
|
263 | : JTDDataDef<S, Record<string, never>>
|