import { keyBy , indexKeyBy } from './helpers' ; export type ArrayInferExtract < T > = T extends Array < infer P > ? P : T ; export type ArrayAlwaysExtract < T > = T extends Array < infer P > ? P : never ; export type ReferenceObjectExtract < T > = { [ key in keyof T ] : ArrayInferExtract < T [ key ] > extends object ? ArrayInferExtract < T [ key ] > : never ; } ; export type ReferenceExtractNames < T > = { [ key in keyof T ] : ArrayInferExtract < T [ key ] > extends object ? key : never ; } ; export type ReferenceArrayExtractNames < T > = { [ key in keyof T ] : T [ key ] extends Array < object > ? key : never ; } ; export type ReferenceExtractAllowNames < T > = { [ key in ReferenceExtractNames < T > [ keyof T ] ] : ReferenceExtractNames < T > [ key ] ; } ; export type ReferenceArrayExtractAllowNames < T > = { [ key in ReferenceArrayExtractNames < T > [ keyof T ] ] : ReferenceExtractNames < T > [ key ] ; } ; example export type ReferenceExtractAllowKeys < T > = { [ key in keyof ReferenceExtractAllowNames < T > ] ? : keyof ReferenceObjectExtract < T > [ key ] ; } ; export type ReferenceArrayExtractAllowKeys < T > = { [ key in keyof ReferenceArrayExtractAllowNames < T > ] ? : keyof ReferenceObjectExtract < T > [ key ] ; } ; function getReferenceIdKey < TReferences > ( referenceKey , idKeys ) { if ( idKeys && idKeys [ referenceKey ] ) { return idKeys [ referenceKey ] ; } else { return '_id' ; } } example param originalReferences param references param idKeys returns export function mergeReferences < TReferences > ( originalReferences , references , idKeys ? ) { for ( const key in references ) { if ( references . hasOwnProperty ( key ) ) { const reference = references [ key ] ; const referenceIdKey = getReferenceIdKey < TReferences > ( key , idKeys ) ; const originalReference = originalReferences [ key ] ; if ( ! originalReference ) { throw new Error ( `original reference must exist when append new reference: ${ key }` ) ; } if ( originalReference instanceof Array ) { const originalReferenceIdIndexMap = indexKeyBy < ReferenceExtractAllowKeys < TReferences > > ( originalReferences [ key ] as any , referenceIdKey ) ; if ( reference instanceof Array ) { reference . forEach ( ( item ) => { const itemId = item [ referenceIdKey ] ; const index = originalReferenceIdIndexMap [ itemId ] ; if ( index >= 0 ) { originalReference [ index ] = { ... originalReference [ index ] , ... item } ; } else { ( originalReferences as any ) [ key ] = [ ... ( originalReferences [ key ] as any ) , item ] ; } } ) ; } else { const itemId = reference [ referenceIdKey ] ; const index = originalReferenceIdIndexMap [ itemId ] ; if ( itemId >= 0 ) { originalReference [ index ] = { ... originalReference [ index ] , ... reference } ; } else { ( originalReferences as any ) [ key ] = [ ... ( originalReferences [ key ] as any ) , reference ] ; } } } else { originalReferences [ key ] = { ... originalReferences [ key ] , ... reference } ; } } } return originalReferences ; } param references param idKeys export function buildReferencesKeyBy < TReferences > ( references , idKeys ? ) { const result = { } ; for ( const key in references ) { if ( references . hasOwnProperty ( key ) ) { const referenceIdKey = getReferenceIdKey < TReferences > ( key , idKeys ) ; const reference = references [ key ] ; if ( reference instanceof Array ) { const originalReferenceIdMap = keyBy < ArrayInferExtract < TReferences > > ( reference , referenceIdKey ) ; ( result as any ) [ key ] = originalReferenceIdMap ; } } } return result ; }