import {AnyArray, Falsy} from '../shared' type DeepReplaceKeysPartialObj = { // If this is an array, we want to flatten it to an object for QGL-like queries [key in keyof Obj]: Obj[key] extends AnyArray ? DeepReplaceKeys : // We need to handle `undefined` for optional objects Exclude extends object ? DeepReplaceKeys : T } export type DeepReplaceKeys = Obj extends object ? DeepReplaceKeysPartialObj : Obj export type DeepPartial = { [P in keyof T]?: DeepPartial } type PickDeepObj< ReplaceType, Obj extends object, ObjQuery extends DeepPartial>, > = { // A key must be in the query object to be added to final object [key in keyof ObjQuery]: key extends keyof Obj ? // If in array, we need to unwrap the objects within for the "ToPick" object, // but not unwrap the "ToPick" query object Obj[key] extends AnyArray ? Array> : // If it's an object, pick it Exclude extends object ? // Check if the object is optional. If it is, we need to unwrap the undefined object // and then rewrap it (to keep the `undefined` types Obj[key] extends Exclude ? PickDeep : | PickDeep, ObjQuery[key]> | undefined : // This trick only really works on `const` objects where falsyness can be eval'd at runtime ObjQuery[key] extends Falsy ? never : Obj[key] : never } export type PickDeep = Obj extends object ? PickDeepObj : Obj export function pickDeep< Obj extends object, ObjQuery extends DeepPartial< DeepReplaceKeys > = DeepPartial>, >( objToPick: Obj, pickObjDeclare: ObjQuery, ): PickDeepObj { // It may be a primitive if (typeof objToPick !== 'object') return objToPick const returnObject: PickDeepObj = {} as never for (const key in pickObjDeclare) { if (key in objToPick) { const oKey = key as never if (Array.isArray(objToPick[oKey])) { returnObject[key] = (objToPick[oKey] as object[]).map( (item: object) => { return pickDeep(item, pickObjDeclare[key] as never) }, ) as never } else if (typeof objToPick[oKey] === 'object') { returnObject[key] = pickDeep( objToPick[oKey], pickObjDeclare[key] as never, ) } else if (pickObjDeclare[key]) returnObject[key] = objToPick[oKey] } } return returnObject }