fork of hey-api/openapi-ts because I need some additional things
at feat/skip-token 118 lines 3.7 kB view raw
1type ObjectType<T> = 2 Extract<T, Record<string, any>> extends never 3 ? Record<string, any> 4 : Extract<T, Record<string, any>>; 5 6type NotArray<T> = T extends Array<any> ? never : T; 7type NotFunction<T> = T extends (...args: Array<any>) => any ? never : T; 8type PlainObject<T> = T extends object 9 ? NotFunction<T> extends never 10 ? never 11 : NotArray<T> extends never 12 ? never 13 : T 14 : never; 15 16type MappersType<T> = { 17 boolean: T extends boolean ? (value: boolean) => Partial<ObjectType<T>> : never; 18 function: T extends (...args: Array<any>) => any 19 ? (value: (...args: Array<any>) => any) => Partial<ObjectType<T>> 20 : never; 21 number: T extends number ? (value: number) => Partial<ObjectType<T>> : never; 22 object?: PlainObject<T> extends never 23 ? never 24 : (value: Partial<PlainObject<T>>, defaultValue: PlainObject<T>) => Partial<ObjectType<T>>; 25 string: T extends string ? (value: string) => Partial<ObjectType<T>> : never; 26} extends infer U 27 ? { [K in keyof U as U[K] extends never ? never : K]: U[K] } 28 : never; 29 30type IsObjectOnly<T> = T extends Record<string, any> | undefined 31 ? Extract<T, string | boolean | number | ((...args: Array<any>) => any)> extends never 32 ? true 33 : false 34 : false; 35 36export type ValueToObject = < 37 T extends 38 | undefined 39 | string 40 | boolean 41 | number 42 | ((...args: Array<any>) => any) 43 | Record<string, any>, 44>( 45 args: { 46 defaultValue: ObjectType<T>; 47 value: T; 48 } & (IsObjectOnly<T> extends true 49 ? { 50 mappers?: MappersType<T>; 51 } 52 : { 53 mappers: MappersType<T>; 54 }), 55) => PlainObject<T>; 56 57const isPlainObject = (value: unknown): value is Record<string, any> => 58 typeof value === 'object' && 59 value !== null && 60 !Array.isArray(value) && 61 typeof value !== 'function'; 62 63const mergeResult = <T>(result: ObjectType<T>, mapped: Record<string, any>): ObjectType<T> => { 64 for (const [key, value] of Object.entries(mapped)) { 65 if (value !== undefined && value !== '') { 66 (result as Record<string, any>)[key] = value; 67 } 68 } 69 return result; 70}; 71 72export const valueToObject: ValueToObject = ({ defaultValue, mappers, value }) => { 73 let result = { ...defaultValue }; 74 75 switch (typeof value) { 76 case 'boolean': 77 if (mappers && 'boolean' in mappers) { 78 const mapper = mappers.boolean as (value: boolean) => Record<string, any>; 79 result = mergeResult(result, mapper(value)); 80 } 81 break; 82 case 'function': 83 if (mappers && 'function' in mappers) { 84 const mapper = mappers.function as ( 85 value: (...args: Array<any>) => any, 86 ) => Record<string, any>; 87 result = mergeResult(result, mapper(value as (...args: Array<any>) => any)); 88 } 89 break; 90 case 'number': 91 if (mappers && 'number' in mappers) { 92 const mapper = mappers.number as (value: number) => Record<string, any>; 93 result = mergeResult(result, mapper(value)); 94 } 95 break; 96 case 'string': 97 if (mappers && 'string' in mappers) { 98 const mapper = mappers.string as (value: string) => Record<string, any>; 99 result = mergeResult(result, mapper(value)); 100 } 101 break; 102 case 'object': 103 if (isPlainObject(value)) { 104 if (mappers && 'object' in mappers && typeof mappers.object === 'function') { 105 const mapper = mappers.object as ( 106 value: Record<string, any>, 107 defaultValue: ObjectType<any>, 108 ) => Partial<ObjectType<any>>; 109 result = mergeResult(result, mapper(value, defaultValue)); 110 } else { 111 result = mergeResult(result, value); 112 } 113 } 114 break; 115 } 116 117 return result as any; 118};