--- /dev/null
+/* eslint-disable @typescript-eslint/no-empty-interface */
+export type SomeJSONSchema = JSONSchemaType<Known, true>
+
+export type PartialSchema<T> = Partial<JSONSchemaType<T, true>>
+
+type JSONType<T extends string, _partial extends boolean> = _partial 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
+}
+
+export type JSONSchemaType<T, _partial extends boolean = false> = (
+ | ((T extends number
+ ? {
+ type: JSONType<"number" | "integer", _partial>
+ } & NumberKeywords
+ : T extends string
+ ? {
+ type: JSONType<"string", _partial>
+ } & StringKeywords
+ : T extends boolean
+ ? {
+ type: "boolean"
+ }
+ : T extends [any, ...any[]]
+ ? {
+ // JSON AnySchema for tuple
+ type: JSONType<"array", _partial>
+ items: {
+ readonly [K in keyof T]-?: JSONSchemaType<T[K]> & Nullable<T[K]>
+ } & {length: T["length"]}
+ minItems: T["length"]
+ } & ({maxItems: T["length"]} | {additionalItems: false})
+ : T extends readonly any[]
+ ? {
+ type: JSONType<"array", _partial>
+ items: JSONSchemaType<T[0]>
+ contains?: PartialSchema<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", _partial>
+ // "required" type does not guarantee that all required properties are listed
+ // it only asserts that optional cannot be listed
+ required: _partial extends true ? Readonly<(keyof T)[]> : Readonly<RequiredMembers<T>[]>
+ additionalProperties?: boolean | JSONSchemaType<T[string]>
+ unevaluatedProperties?: boolean | JSONSchemaType<T[string]>
+ properties?: _partial extends true ? Partial<PropertiesSchema<T>> : PropertiesSchema<T>
+ patternProperties?: {[Pattern in string]?: JSONSchemaType<T[string]>}
+ propertyNames?: JSONSchemaType<string>
+ dependencies?: {[K in keyof T]?: Readonly<(keyof T)[]> | PartialSchema<T>}
+ dependentRequired?: {[K in keyof T]?: Readonly<(keyof T)[]>}
+ dependentSchemas?: {[K in keyof T]?: PartialSchema<T>}
+ minProperties?: number
+ maxProperties?: number
+ }
+ : T extends null
+ ? {
+ nullable: true
+ }
+ : never) & {
+ allOf?: Readonly<PartialSchema<T>[]>
+ anyOf?: Readonly<PartialSchema<T>[]>
+ oneOf?: Readonly<PartialSchema<T>[]>
+ if?: PartialSchema<T>
+ then?: PartialSchema<T>
+ else?: PartialSchema<T>
+ not?: PartialSchema<T>
+ })
+ // these two unions allow arbitrary unions of types
+ | {
+ anyOf: readonly JSONSchemaType<T, _partial>[]
+ }
+ | {
+ oneOf: readonly JSONSchemaType<T, _partial>[]
+ }
+ // this union allows for { type: (primitive)[] } style schemas
+ | ({
+ type: (T extends number
+ ? JSONType<"number" | "integer", _partial>
+ : T extends string
+ ? JSONType<"string", _partial>
+ : T extends boolean
+ ? JSONType<"boolean", _partial>
+ : never)[]
+ } & (T extends number
+ ? NumberKeywords
+ : T extends string
+ ? StringKeywords
+ : T extends boolean
+ ? unknown
+ : never))
+) & {
+ [keyword: string]: any
+ $id?: string
+ $ref?: string
+ $defs?: {
+ [Key in string]?: JSONSchemaType<Known, true>
+ }
+ definitions?: {
+ [Key in string]?: JSONSchemaType<Known, true>
+ }
+}
+
+type Known = KnownRecord | [Known, ...Known[]] | Known[] | number | string | boolean | null
+
+interface KnownRecord extends Record<string, Known> {}
+
+export type PropertiesSchema<T> = {
+ [K in keyof T]-?: (JSONSchemaType<T[K]> & Nullable<T[K]>) | {$ref: string}
+}
+
+export type RequiredMembers<T> = {
+ [K in keyof T]-?: undefined extends T[K] ? never : K
+}[keyof T]
+
+type Nullable<T> = undefined extends T
+ ? {
+ nullable: true
+ const?: never // 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
+ }
+ : {
+ const?: T
+ enum?: Readonly<T[]>
+ default?: T
+ }