type JSONSchema2TS<T> = T extends { type: 'object', properties?: infer Prop, required?: infer Req }
? unknown extends Prop
? Record<string, unknown>
: Omit<{ [P in keyof Prop as P extends Req[keyof Req] ? P : never]: JSONSchema2TS<Prop[P]> } & { [P in keyof Prop as P extends Req[keyof Req] ? never : P]?: JSONSchema2TS<Prop[P]> }, never>
: T extends { type: 'array', items?: infer Item }
? unknown extends Item
? unknown[]
: JSONSchema2TS<Item>[]
: T extends { type: infer Type, enum?: infer Enum }
? unknown extends Enum
? Type extends 'string' ? string
: Type extends 'number' ? number
: Type extends 'boolean' ? boolean
: never
: Enum[keyof Enum & number]
: never
Solution by 2083335157 #35181
type MergeIntersection<I> = { [K in keyof I]: I[K] }
type JSONSchema2TS<V> = V extends { enum: infer T extends any[] }
? T[number]
: V extends { type: 'object' }
? V extends { properties: infer O }
? V extends { required: infer P extends any[] }
? MergeIntersection<
{
[K in keyof O as K extends P[number] ? K : never]: JSONSchema2TS<
O[K]
>
} & {
[K in keyof O as K extends P[number] ? never : K]?: JSONSchema2TS<
O[K]
>
}
>
: { [K in keyof O]?: JSONSchema2TS<O[K]> }
: { [Q: string]: unknown }
: V extends { type: 'array' }
? V extends { items: infer I }
? JSONSchema2TS<I>[]
: unknown[]
: V extends { type: 'boolean' }
? boolean
: V extends { type: 'number' }
? number
: V extends { type: 'string' }
? string
: never
Solution by Keith-Web3 #34785
type Schema = {
type: "string" | "number" | "boolean" | "object" | "array";
enum?: unknown[];
properties?: Record<string, Schema>;
items?: Schema;
required?: string[];
};
type Merge<T extends object> = {
[P in keyof T]: T[P];
};
type JSONSchema2TS<T extends Schema> = T["enum"] extends unknown[]
? T["enum"][number]
: T["type"] extends "string"
? string
: T["type"] extends "number"
? number
: T["type"] extends "boolean"
? boolean
: T["type"] extends "object"
? T["properties"] extends Record<string, Schema>
? T["required"] extends unknown[]
? Merge<
{
[P in keyof T["properties"] & T["required"][number]]: JSONSchema2TS<T["properties"][P]>;
} & {
[P in keyof T["properties"]]?: JSONSchema2TS<T["properties"][P]>;
}
>
: {
[P in keyof T["properties"]]?: JSONSchema2TS<T["properties"][P]>;
}
: Record<string, unknown>
: T["type"] extends "array"
? T["items"] extends Schema
? JSONSchema2TS<T["items"]>[]
: unknown[]
: never;
Solution by yukicountry #34402
// your answers
type RequiredByKeys<
T extends object,
K extends string | number | symbol
> = Omit<
{
[P in keyof T as P extends K ? P : never]-?: T[P];
} & {
[P in keyof T as P extends K ? never : P]?: T[P];
},
never
>;
type Primitive = {
string: string;
number: number;
boolean: boolean;
};
type JsonSchema = {
type: keyof Primitive | "object" | "array";
enum?: any[];
properties?: Record<string, JsonSchema>;
items?: JsonSchema;
required?: string[];
};
type JSONSchema2TS<T extends JsonSchema> = T["enum"] extends any[]
? T["enum"][number]
: T["type"] extends keyof Primitive
? Primitive[T["type"]]
: T["type"] extends "array"
? T["items"] extends JsonSchema
? JSONSchema2TS<T["items"]>[]
: unknown[]
: T["type"] extends "object"
? T["properties"] extends Record<string, JsonSchema>
? RequiredByKeys<
{ [K in keyof T["properties"]]: JSONSchema2TS<T["properties"][K]> },
T["required"] extends string[] ? T["required"][number] : never
>
: Record<string, unknown>
: never;
Solution by chenqy-yh #34341
type TypeProperty = 'string' | 'number' | 'boolean' | 'object' | 'array'
type Properties = {
[K in string]?: JSONSchema
}
type JSONSchema = {
type: TypeProperty
enum?: unknown[]
items?: JSONSchema,
properties?: Properties
required?: string[]
}
type Primitive = {
string: string
boolean: boolean
number: number
}
type JSONSchema2TS<T extends JSONSchema> = T['enum'] extends any[] ? T['enum'][number] : T['type'] extends keyof Primitive ? Primitive[T['type']] : T['type'] extends 'array' ? T['items'] extends JSONSchema ? JSONSchema2TS<T['items']>[] : unknown[] : T['type'] extends 'object' ? T['properties'] extends Properties ? Omit<{
[K in Exclude<keyof T['properties'], T['required'] extends string[] ? T['required'][number] : never>]?: T['properties'][K] extends JSONSchema ? JSONSchema2TS<T['properties'][K]> : never
} & {
[K in Extract<keyof T['properties'], T['required'] extends string[] ? T['required'][number] : never>]: T['properties'][K] extends JSONSchema ? JSONSchema2TS<T['properties'][K]> : never
}, never> : Record<string, unknown> : never
Solution by ouzexi #34151
type PrimitiveTypes = {
string: string, number: number, boolean: boolean
}
type JSONS = {
type: string,
enum?: any[],
properties?: Record<string, JSONS>,
items?: JSONS,
required?: string[]
}
type PrimitiveType<T extends JSONS> = T['type'] extends keyof PrimitiveTypes ? T['enum'] extends any[] ? T['enum'][number] : PrimitiveTypes[T['type']] : never
type ObjectType<T extends Record<string, JSONS>> = {
[key in keyof T]?: JSONSchema2TS<T[key]>
}
type RequiredType<T extends JSONS> = T['properties'] extends {} ? T['required'] extends string[] ? RequiredByKeys<ObjectType<T['properties']>, T['required']> : ObjectType<T['properties']> : never
type RequiredByKeys<T , K extends string[]> = Pick<T & Required<Pick<T, K[number] & keyof T>>, keyof T>
type JSONSchema2TS<T extends JSONS> = T['type'] extends keyof PrimitiveTypes ? PrimitiveType<T> :
T['type'] extends 'object' ? T['properties'] extends {} ? RequiredType<T> : Record<string, unknown> :
T['type'] extends 'array' ? T['items'] extends {} ? Array<JSONSchema2TS<T['items']>> : unknown[] :
never
Solution by moonshadow-miao #33918
type JSONSchema2TS<T>
= T extends {type: 'string', enum?: infer E} ? E extends string[] ? E[number] : string
: T extends {type: 'number', enum?: infer E} ? E extends number[] ? E[number] : number
: T extends {type: 'boolean'} ? boolean
: T extends {type: 'object', properties?: infer P, required?: infer R}
? P extends object ? ReturnType<<O extends (
{[K in keyof P as [K] extends [R extends string[] ? R[number] : never] ? never : K]?: JSONSchema2TS<P[K]>}
& {[K in keyof P as [K] extends [R extends string[] ? R[number] : never] ? K : never]: JSONSchema2TS<P[K]>}
)>() => {[K in keyof O]: O[K]}>
: Record<string, unknown>
: T extends {type: 'array', items?: infer I} ? I extends object ? JSONSchema2TS<I>[] : unknown[]
: T
Solution by teamchong #33591
type JSONPrimitiveType2TSType = {
'string': string
'number': number
'boolean': boolean
}
type JSONPrimitiveType = keyof JSONPrimitiveType2TSType
type JSONType = JSONPrimitiveType | 'object' | 'array'
type JSONSchema = {
type: JSONType,
enum?: JSONPrimitiveType2TSType[JSONPrimitiveType][]
properties?: Record<string, JSONSchema>
items?: JSONSchema
required?: string[]
}
type JSONObject2TS<T extends JSONSchema> =
T['properties'] extends Record<string, JSONSchema>
? T['required'] extends string[]
? Omit<{
[k in Exclude<keyof T['properties'], T['required'][number]>]?: JSONSchema2TS<T['properties'][k]>
} & {
[k in T['required'][number]]: JSONSchema2TS<T['properties'][k & keyof T['properties']]>
}, never>
: {
[k in keyof T['properties']]?: JSONSchema2TS<T['properties'][k]>
}
: Record<string, unknown>
type JSONArray2TS<T extends JSONSchema> =
T['items'] extends JSONSchema
? JSONSchema2TS<T['items']>[]
: unknown[]
type JSONSchema2TS<T extends JSONSchema> =
T['type'] extends JSONPrimitiveType
? T['enum'] extends JSONPrimitiveType2TSType[JSONPrimitiveType][]
? T['enum'][number]
: JSONPrimitiveType2TSType[T['type']]
: T['type'] extends 'object'
? JSONObject2TS<T>
: T['type'] extends 'array'
? JSONArray2TS<T>
: never
Solution by hui0808 #33434
写完自己都晕了。
type Json = {
type: TypeAll;
enum?: any[];
items?: Json;
properties?: Record<PropertyKey, Json>;
required?: Array<any>;
};
type PrimitiveTypeMap = {
string: string;
number: number;
boolean: boolean;
};
type PrimitiveType = "string" | "number" | "boolean";
type ObjectType = "object";
type ArrayType = "array";
type CompositeType = ObjectType | ArrayType;
type TypeAll = PrimitiveType | CompositeType;
type IntersectionToObj<T> = {
[P in keyof T]: T[P];
};
type JSONSchema2TS<T extends Json> = T["type"] extends PrimitiveType
? T["enum"] extends any[]
? T["enum"][number]
: PrimitiveTypeMap[T["type"]]
: T["type"] extends ObjectType
? T["properties"] extends Record<PropertyKey, Json>
? T["required"] extends any[]
? IntersectionToObj<
{
[P in T["required"][number]]: JSONSchema2TS<T["properties"][P]>;
} & {
[P in Exclude<
keyof T["properties"],
T["required"][number]
>]?: JSONSchema2TS<T["properties"][P]>;
}
>
: { [P in keyof T["properties"]]?: JSONSchema2TS<T["properties"][P]> }
: Record<string, unknown>
: T["type"] extends ArrayType
? T["items"] extends Json
? JSONSchema2TS<T["items"]>[]
: unknown[]
: T;
Solution by Vampirelee #32577
type JSONSchema2TS<T> = T extends { type: infer U }
? U extends "string"
? T extends { enum: infer E extends Array<unknown> }
? E[number]
: string
: U extends "number"
? T extends { enum: infer E extends Array<unknown> }
? E[number]
: number
: U extends "boolean"
? boolean
: U extends "object"
? T extends { properties: infer P }
? T extends { required: infer R extends any[] }
? Omit<
{ [K in R[number]]: JSONSchema2TS<P[K]> } & {
[K in Exclude<keyof P, R[number]>]?: JSONSchema2TS<P[K]>;
},
never
>
: { [K in keyof P]?: JSONSchema2TS<P[K]> }
: Record<string, unknown>
: U extends "array"
? T extends { items: infer I }
? JSONSchema2TS<I>[]
: unknown[]
: never
: never;
Solution by vangie #32211
type SimpleType = {
string: string,
number: number,
boolean: boolean,
object: Record<string, unknown>
array: unknown[]
}
type HandleSimpleType<T, Type> = T extends { enum: unknown[] }
? T['enum'][number]
: Type extends keyof SimpleType
? SimpleType[Type]
: never
type Copy<T> = {
[K in keyof T]: T[K]
}
type HandleProperties<T, Required extends string[] = []> = Copy<{
[K in keyof T as K extends Required[number] ? K : never]: JSONSchema2TS<T[K]>
} & {
[K in keyof T as K extends Required[number] ? never : K]?: JSONSchema2TS<T[K]>
}>
type JSONSchema2TS<T> = T extends { properties: infer Properties }
? (T extends { required: infer Required extends string[] } ? HandleProperties<Properties, Required> : HandleProperties<Properties>)
: T extends { items: infer Array }
? JSONSchema2TS<Array>[]
: T extends { type: infer Type }
? HandleSimpleType<T, Type>
: never
先将要处理的类型分为简单、复杂类型,带有 properties
的对象和带有 items
的数组为复杂类型,其他的为简单类型。
然后 JSONSchema2TS
就可以简单的分为三步了了,前两步处理复杂类型的对象和数组,如果不是这两个类型,最后一步就可以用简单类型对象 SimpleType
返回映射类型就可以了。
type SimpleType = {
string: string,
number: number,
boolean: boolean,
object: Record<string, unknown>
array: unknown[]
}
接下来再看一下复杂数组类型,可以发现 items
里对应的数组子项类型对象,其实是可以递归的用 JSONSchema2TS
处理的,所以这里可以这样处理:
T extends { items: infer Array }
? JSONSchema2TS<Array>[]
: ...
最后再看一下复杂对象类型,这里需要处理 required
和 properties
,required
是一个字符串数组,表示 properties
中的哪些属性是必须的,所以这里需要用到一个条件类型,将 properties
中的属性分为必须和可选两种类型,然后再将两种类型合并为一个对象类型。这里比较重要的代码是:
type Copy<T> = {
[K in keyof T]: T[K]
}
type HandleProperties<T, Required extends string[] = []> = Copy<{
[K in keyof T as K extends Required[number] ? K : never]: JSONSchema2TS<T[K]>
} & {
[K in keyof T as K extends Required[number] ? never : K]?: JSONSchema2TS<T[K]>
}>
HandleProperties
将 properties
中的属性根据 Required
分为必须和可选两种类型,但是这样处理后的类型代码是这样的:
{ a: number } & { b?: string }
不符合要求,所以要将这样的代码进行合并,这里用 Copy
来处理,经过它处理后,上面的示例会变成:
{
a: number,
b?: string
}
Solution by woai3c #31881
export type Merge<T, U, R = Omit<T, keyof U> & U> = { [K in keyof R]: R[K] };
type TypeMap = {
'string': string
'number': number
'boolean': boolean
'object': Record<string, unknown>
'array': unknown[]
}
type JSONSchema = {
type: keyof TypeMap
properties?: Record<PropertyKey, JSONSchema>
items?: JSONSchema
enum?: (string | number | boolean)[]
required?: PropertyKey[]
}
type JSONSchema2TS<T extends JSONSchema, Type extends keyof TypeMap = T['type']> =
Type extends 'object'
? T['properties'] extends infer P extends Record<PropertyKey, JSONSchema>
? T['required'] extends infer R extends PropertyKey[]
? Merge<
{[K in Exclude<keyof P, R[number]>]?: JSONSchema2TS<P[K]>},
{[K in R[number]]: JSONSchema2TS<P[K]>}
>
: {[K in keyof P]?: JSONSchema2TS<P[K]>}
: TypeMap[Type]
: Type extends 'array'
? T['items'] extends infer I extends JSONSchema
? JSONSchema2TS<I>[]
: TypeMap[Type]
: T['enum'] extends infer E extends unknown[]
? E[number]
: TypeMap[Type]
Solution by milletlovemouse #30969
// your answers
// utils
type TypeMap = {
"string": string,
"number": number
"boolean": boolean
"object": Record<string, unknown>
"array": Array<unknown>
}
type SupportedType = keyof TypeMap
type Flatten<T> = { [P in keyof T]: T[P]}
// main
type JSONSchema2TS<T> =
T extends { "type": infer Type extends SupportedType } ?
T extends { "enum" : infer E extends unknown[]} ?
E[number]
:
T extends { "items": infer I } ?
JSONSchema2TS<I>[]
:
T extends { "properties": infer P extends Record<string, unknown> } ?
T extends { "required": infer Required extends (keyof P)[]} ?
Flatten<{ [Key in Exclude<keyof P, Required[number]>]?: JSONSchema2TS<P[Key]>} & { [Key in Extract<keyof P, Required[number]>]: JSONSchema2TS<P[Key]>}>
:
{ [Key in keyof P]?: JSONSchema2TS<P[Key]>}
:
TypeMap[Type]
: never
Solution by Kakeru-Miyazaki #30923
type PrimitiveMap = {
boolean: boolean
number: number
string: string
null: null
}
type Primitives = keyof PrimitiveMap
type PrimitiveSchema<P extends Primitives> = {
type: P
enum?: PrimitiveMap[P][]
}
type PrimitiveSchema2TS<P extends Primitives, S extends PrimitiveSchema<P>> = S['enum'] extends infer E extends any[]
? E[number]
: PrimitiveMap[P]
type PrimitivesSchema = { [K in Primitives]: PrimitiveSchema<K> }[Primitives]
type ObjectSchema = {
type: 'object'
properties?: {
[K in string]: {
type: SchemaTypes
}
}
required?: string[]
}
type Prettify<T> = { [K in keyof T]: T[K] } & {}
type ObjectSchema2TS<S extends ObjectSchema> = ([unknown] | [undefined]) extends [S['properties']]
? Record<string, unknown>
: [keyof S['properties'], S['properties']] extends infer T extends [string, Record<string, Schema>]
? { [K in T[0]]: JSONSchema2TS<T[1][K]> } extends infer Raw
? S['required'] extends infer R extends string[]
? Prettify<Pick<Raw, R[number] extends keyof Raw ? R[number] : never> & Partial<Omit<Raw, R[number]>>>
: Partial<Raw>
: never
: never
type ArraySchema = {
type: 'array'
items?: Schema
}
type ArraySchema2TS<S extends ArraySchema> = ([unknown] | [undefined]) extends [S['items']]
? unknown[]
: S['items'] extends Schema
? JSONSchema2TS<S['items']>[]
: never
type SchemaTypes = Schema['type']
type Schema = PrimitivesSchema | ObjectSchema | ArraySchema
type JSONSchema2TS<S extends Schema> = [S['type'], S] extends infer T extends [Primitives, PrimitivesSchema]
? PrimitiveSchema2TS<T[0], T[1]>
: [S['type'], S] extends infer T extends ['object', ObjectSchema]
? ObjectSchema2TS<T[1]>
: [S['type'], S] extends infer T extends ['array', ArraySchema]
? ArraySchema2TS<T[1]>
: never
Solution by DevilTea #30861
type TypeMap = {
string: string
number: number
boolean: boolean
object: Record<string, unknown>
array: unknown[]
}
type JSONObject = {
type: keyof TypeMap
enum?: unknown[]
properties?: Record<PropertyKey, JSONObject>
required?: PropertyKey[]
items?: JSONObject
}
type MakeObject<
P extends Record<PropertyKey, JSONObject>,
R extends PropertyKey,
U extends PropertyKey = Exclude<keyof P, R>,
> = Omit<
{
[K in U]?: JSONSchema2TS<P[K]>
} & {
[K in R]: JSONSchema2TS<P[K]>
},
never
>
type JSONSchema2TS<T extends JSONObject> = T['type'] extends 'object'
? T['properties'] extends object
? MakeObject<
T['properties'],
T['required'] extends any[] ? T['required'][number] : never
>
: TypeMap['object']
: T['type'] extends 'array'
? T['items'] extends JSONObject
? JSONSchema2TS<T['items']>[]
: TypeMap['array']
: T['enum'] extends TypeMap[T['type']][]
? T['enum'][number]
: TypeMap[T['type']]
Solution by FlareZh #30823
type PrimitivesMap = {
'string': string
'number': number
'boolean': boolean
'object': object
'array': Array<unknown>
}
interface Model {
type: keyof PrimitivesMap
enum?: Array<unknown>
items?: Model
properties?: { [x: string]: Model }
required?: string[]
}
type HandleObject<T extends Model, K extends T['properties'] = T extends { properties: { [x: string]: Model } } ? T['properties'] : never> =
[K] extends [never] ? { [x: string]: unknown }
: T['required'] extends any[] ?
Omit<{
[P in T['required'][number]]: JSONSchema2TS<T['properties'][P]>
} & {
[P in Exclude<keyof K, T['required'][number]>]?: JSONSchema2TS<T['properties'][P]>
}, never>
: {
[P in keyof T['properties']]?: JSONSchema2TS<T['properties'][P]>
}
type HandleArray<T extends Model> = T['items'] extends Model ? JSONSchema2TS<T['items']>[] : unknown[]
type JSONSchema2TS<T extends Model> =
T['enum'] extends any[] ? T['enum'][number]
: T['type'] extends 'array' ? HandleArray<T>
: T['type'] extends 'object' ? HandleObject<T>
: PrimitivesMap[T['type']]
Solution by hesoso #29996
type Schema = {
type: 'string',
enum?: string[]
} | {
type: 'boolean',
enum?: boolean[]
} | {
type: 'number'
enum?: number[]
} | {
type: 'object',
properties?: Record<string, Schema>
required?: unknown[]
} | {
type: 'array',
items?: Schema
}
type GetEnum<T, Fallback> = T extends Fallback[] ? T[number] : Fallback
type JSONSchema2TS<T extends Schema> =
T extends { type: 'string' }
? GetEnum<T['enum'], string>
: T extends { type: 'boolean' }
? GetEnum<T['enum'], boolean>
: T extends { type: 'number' }
? GetEnum<T['enum'], number>
: T extends { type: 'object' }
? T['properties'] extends Record<string, Schema>
? T['required'] extends any[]
? Omit<{
[x in T['required'][number] & keyof T['properties']]: JSONSchema2TS<T['properties'][x]>
} & {
[x in keyof Omit<T['properties'], T['required'][number]>]?: JSONSchema2TS<T['properties'][x]>
}, never>
: { [x in ((keyof T['properties']))]?: JSONSchema2TS<T['properties'][x]> }
: Record<string, unknown>
: T extends { type: 'array' }
? Array<T['items'] extends Schema ? JSONSchema2TS<T['items']> : unknown>
: never
Solution by hinsxd #29576
type JSONSchema2TS<T>
// enum
= T extends { enum: infer Enum extends unknown[] } ? Enum[number]
// typed array
: T extends { items: infer Items } ? JSONSchema2TS<Items>[]
// non-empty object
: T extends { properties: infer Props extends object }
? T extends { required: infer Required extends string[] }
? Omit<
& { [K in keyof Props & Required[number]]: JSONSchema2TS<Props[K]> }
& { [K in Exclude<keyof Props, Required[number]>]?: JSONSchema2TS<Props[K]> }
, never>
: { [K in keyof Props]?: JSONSchema2TS<Props[K]> }
// primitives, empty object, untyped array
: T extends { type: infer Type extends 'number' | 'string' | 'boolean' | 'object' | 'array' }
? { number: number; string: string; boolean: boolean; object: Record<string, unknown>; array: unknown[] }[Type]
: never
Solution by Alexsey #29373
interface PrimitiveMap {
string: string
number: number
boolean: boolean
}
interface Schema {
type: keyof PrimitiveMap | 'object' | 'array'
enum?: string[] | number[]
properties?: Record<string, Schema>
items?: Schema
required?: string[]
}
type Merge<T> = {
[Key in keyof T]: T[Key]
}
type RequiredWith<T extends Record<string, unknown>, Keys extends keyof T> =
Merge<Required<Pick<T, Keys>> & Omit<T, Keys>>
type JSONSchema2TS<T extends Schema> =
T['type'] extends keyof PrimitiveMap
? T['enum'] extends unknown[]
? T['enum'][number]
: PrimitiveMap[T['type']]
: T['type'] extends 'object'
? T['properties'] extends Record<string, Schema>
? RequiredWith<{
[Key in keyof T['properties']]?: JSONSchema2TS<T['properties'][Key]>
}, T['required'] extends string[] ? T['required'][number] : never>
: Record<string, unknown>
: T['items'] extends Schema
? JSONSchema2TS<T['items']>[]
: unknown[]
Solution by drylint #27435
type Expand<T> = T extends object ? {[key in keyof T]: Expand<T[key]>} : T;
type PrimitiveTypes = { string: string; number: number; boolean: boolean; array: unknown[] }
type JSONSchema2TS<T extends { type: string }> =
Expand<
(T extends { enum: unknown[] } ? T["enum"][number] :
T extends { properties: any } ? { [K in keyof T["properties"]]?: JSONSchema2TS<T["properties"][K]> } :
T extends { items: any } ? Array<JSONSchema2TS<T["items"]>> :
T extends { type: "object" } ? Record<string, unknown> :
PrimitiveTypes[T["type"] & keyof PrimitiveTypes])
&
(T extends { required: string[]; properties: any } ?
{ [key in T["required"][number]]: JSONSchema2TS<T["properties"][key]> } : {})
>
Solution by mwashief #27236
type Item = {
type: string
enum?: any[]
items?: Item
properties?: Record<string, Item>
required?: string[]
}
type JSONSchema2TS<T extends Item> =
T['enum'] extends PropertyKey[]
? T['enum'][number]
: T['type'] extends 'string'
? string
: T['type'] extends 'number'
? number
: T['type'] extends 'boolean'
? boolean
: T['type'] extends 'array'
? T['items'] extends Item
? JSONSchema2TS<T['items']>[]
: unknown[]
: T['type'] extends 'object'
? T['properties'] extends Record<string, Item>
? T['required'] extends string[]
? Omit<{
[K in T['required'][number]]: JSONSchema2TS<T['properties'][K]>
} & {
[K in keyof T['properties']]?: JSONSchema2TS<T['properties'][K]>
}, never>
: {
[K in keyof T['properties']]?: JSONSchema2TS<T['properties'][K]>
}
: Record<string, unknown>
: never
Solution by Minato1123 #27203
type Primitives = {
string: string;
number: number;
boolean: boolean;
};
type HandlePrimitives<T, Type extends keyof Primitives> = T extends {
enum: unknown[];
}
? T['enum'][number]
: Primitives[Type];
type HandleObject<T> = T extends {
properties: infer Properties extends Record<string, unknown>;
}
? T extends { required: infer Required extends unknown[] }
? Omit<
{
[K in Required[number] & keyof Properties]: JSONSchema2TS<
Properties[K]
>;
} & {
[K in Exclude<keyof Properties, Required[number]>]?: JSONSchema2TS<
Properties[K]
>;
},
never
>
: {
[K in keyof Properties]?: JSONSchema2TS<Properties[K]>;
}
: Record<string, unknown>;
type HandleArray<T> = T extends { items: infer Items }
? JSONSchema2TS<Items>[]
: unknown[];
type JSONSchema2TS<T> = T extends { type: infer Type }
? Type extends keyof Primitives
? HandlePrimitives<T, Type>
: Type extends 'object'
? HandleObject<T>
: HandleArray<T>
: never;
Solution by JohnLi1999 #26864
For some reason the last test fails, but I feel like it shouldn't. https://tsplay.dev/N5A0MN
type SomeObject = {
[k: string]: unknown
}
type NumberParser<T extends SomeObject> = T extends {
enum: readonly number[];
}
? T["enum"][number]
: number;
type StringParser<T extends SomeObject> = T extends {
enum: readonly string[];
}
? T["enum"][number]
: string;
type BaseTypeParser<T extends ValidInput> = T["type"] extends "string"
? StringParser<T>
: T["type"] extends "number"
? NumberParser<T>
: boolean;
type ObjParser<T extends SomeObject> = T extends {
properties: unknown
}
? { [K in keyof T["properties"]]?: JSONSchema2TS<T["properties"][K]> } & (T extends {required: ReadonlyArray<keyof T['properties']>} ? { [K in T['required'][number]]-?: JSONSchema2TS<T['properties'][K]>} : unknown)
: Record<string, unknown>;
type ArrayParser<T extends SomeObject> = T extends {
items: unknown
} ? JSONSchema2TS<T['items']>[] : unknown[]
type ComplexTypes = 'object' | 'array'
type ValidTypes = BaseTypes | ComplexTypes;
type BaseTypes = "string" | "number" | "boolean";
type IsValidInput<T> = T extends { type: ValidTypes } ? true : false;
type ValidInput = { type: ValidTypes };
type ParseValidInput<T extends ValidInput> = T["type"] extends BaseTypes
? BaseTypeParser<T>
: T['type'] extends 'array' ? ArrayParser<T> : ObjParser<T>;
type JSONSchema2TS<T> = IsValidInput<T> extends true
? ParseValidInput<T & ValidInput>
: never;
Solution by amsuarez99 #26740
type Merge<T> = {
[K in keyof T]:T[K]
}
type RequireByKeys<T, KS extends keyof T> = Merge< Required<Pick<T,KS>>& Omit<T,KS>>
type JSONSchema2TS<T> =
T extends {type: "string"}?
T extends {enum:string[] }? T['enum'][number]:string :
T extends {type:"number"}?
T extends {enum:number[]}? T['enum'][number]:number:
T extends {type:"boolean"}?
boolean:
T extends {type: "object"}?
T extends {properties:any}?
RequireByKeys<{[K in keyof T['properties']]?:JSONSchema2TS<T['properties'][K]>}, T extends {required:Array<keyof T['properties']>}?T['required'][number]: never >
:Record<string,unknown>:
T extends {type: "array"}?
T extends {items:any}? Array<JSONSchema2TS<T['items']>>:unknown[]
:never
Solution by jiangshanmeta #26657
type PrimitiveTypes = {
string: string
number: number
boolean: boolean
}
type ResolvePrimitiveType<T extends { type: string }> = T extends { enum: infer E extends unknown[] }
? E[number]
: PrimitiveTypes[T['type'] & keyof PrimitiveTypes]
type ResolveObject<T> = T extends { properties: infer P }
? T extends { required: infer R extends unknown[] }
? MergeIntersection<Required<ResolveProperties<Pick<T['properties'], R[number] & keyof P>>> & ResolveProperties<Omit<T['properties'], R[number] & keyof P>>>
: ResolveProperties<T['properties']>
: Record<string, unknown>
type ResolveProperties<T> = {
[K in keyof T]?: JSONSchema2TS<T[K]>
}
type MergeIntersection<T> = {
[K in keyof T]: T[K]
}
type ResolveArray<T> = T extends { items: infer I }
? JSONSchema2TS<I>[]
: unknown[]
type JSONSchema2TS<T> = T extends { type: infer Type extends string }
? Type extends keyof PrimitiveTypes
? ResolvePrimitiveType<T>
: Type extends "object"
? ResolveObject<T>
: Type extends 'array'
? ResolveArray<T>
: never
: never
Solution by Sun79 #26466
type JSONSchema = { type: string, enum?: any[], items?: JSONSchema, properties?: { [x: string]: JSONSchema }, required?: string[] }
type Primitive<T> = T extends `string` ? string :
T extends `number` ? number :
T extends `boolean` ? boolean :
T;
type CombineObject<T extends object> = { [P in keyof T]: T[P] };
type Include<T, U> = T extends any[] ? U extends T[number] ? true : false : false;
type JSONSchema2TS<T extends JSONSchema> =
//数组
T[`type`] extends `array` ?
(T[`items`] extends JSONSchema ?
JSONSchema2TS<T[`items`]>[] :
unknown[]) :
//结构体
T[`type`] extends `object` ?
(T[`properties`] extends object ?
(CombineObject<
{ [P in keyof T[`properties`]as Include<T[`required`], P> extends true ? never : P]?: JSONSchema2TS<T[`properties`][P]> }
&
{ [P in keyof T[`properties`]as Include<T[`required`], P> extends true ? P : never]: JSONSchema2TS<T[`properties`][P]> }>) :
Record<string, unknown>) :
//原子
T[`enum`] extends any[] ? T[`enum`][number] : Primitive<T[`type`]>;
Solution by E-uler #26443