type ObjectType = Extract> extends never ? Record : Extract>; type NotArray = T extends Array ? never : T; type NotFunction = T extends (...args: Array) => any ? never : T; type PlainObject = T extends object ? NotFunction extends never ? never : NotArray extends never ? never : T : never; type MappersType = { boolean: T extends boolean ? (value: boolean) => Partial> : never; function: T extends (...args: Array) => any ? (value: (...args: Array) => any) => Partial> : never; number: T extends number ? (value: number) => Partial> : never; object?: PlainObject extends never ? never : (value: Partial>, defaultValue: PlainObject) => Partial>; string: T extends string ? (value: string) => Partial> : never; } extends infer U ? { [K in keyof U as U[K] extends never ? never : K]: U[K] } : never; type IsObjectOnly = T extends Record | undefined ? Extract) => any)> extends never ? true : false : false; export type ValueToObject = < T extends | undefined | string | boolean | number | ((...args: Array) => any) | Record, >( args: { defaultValue: ObjectType; value: T; } & (IsObjectOnly extends true ? { mappers?: MappersType; } : { mappers: MappersType; }), ) => PlainObject; const isPlainObject = (value: unknown): value is Record => typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value !== 'function'; const mergeResult = (result: ObjectType, mapped: Record): ObjectType => { for (const [key, value] of Object.entries(mapped)) { if (value !== undefined && value !== '') { (result as Record)[key] = value; } } return result; }; export const valueToObject: ValueToObject = ({ defaultValue, mappers, value }) => { let result = { ...defaultValue }; switch (typeof value) { case 'boolean': if (mappers && 'boolean' in mappers) { const mapper = mappers.boolean as (value: boolean) => Record; result = mergeResult(result, mapper(value)); } break; case 'function': if (mappers && 'function' in mappers) { const mapper = mappers.function as ( value: (...args: Array) => any, ) => Record; result = mergeResult(result, mapper(value as (...args: Array) => any)); } break; case 'number': if (mappers && 'number' in mappers) { const mapper = mappers.number as (value: number) => Record; result = mergeResult(result, mapper(value)); } break; case 'string': if (mappers && 'string' in mappers) { const mapper = mappers.string as (value: string) => Record; result = mergeResult(result, mapper(value)); } break; case 'object': if (isPlainObject(value)) { if (mappers && 'object' in mappers && typeof mappers.object === 'function') { const mapper = mappers.object as ( value: Record, defaultValue: ObjectType, ) => Partial>; result = mergeResult(result, mapper(value, defaultValue)); } else { result = mergeResult(result, value); } } break; } return result as any; };