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