Skip to content

Commit

Permalink
fix: deepMerge util (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakex7 committed Jan 31, 2024
1 parent 5ad9f65 commit c17f39c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 14 deletions.
62 changes: 62 additions & 0 deletions src/__tests__/deepMerge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { deepMerge } from '../utils/deepMerge'
import { defaultTheme, emptyStyles } from '../contexts/KeyboardContext'

describe('deepMerge tests', () => {
const newTheme = {
container: '#000',
emoji: {
selected: '#fff',
},
category: undefined,
}
const newStyles = {
container: { backgroundColor: '#000' },
emoji: {
selected: {
backgroundColor: '#000',
transform: [{ rotate: '45deg' }],
},
},
}

it('should merge theme properly', () => {
expect(deepMerge(defaultTheme, newTheme)).toStrictEqual({
...defaultTheme,
container: newTheme.container,
emoji: {
...defaultTheme.emoji,
selected: newTheme.emoji.selected,
},
})
})

it('should merge styles properly', () => {
expect(deepMerge(emptyStyles, newStyles)).toStrictEqual({
...emptyStyles,
container: newStyles.container,
emoji: {
...emptyStyles.emoji,
selected: newStyles.emoji.selected,
},
})
})

it('should merge theme properly with undefined as property', () => {
const themeWithUndefined = {
...newTheme,
search: { text: undefined },
}
expect(deepMerge(defaultTheme, themeWithUndefined)).toStrictEqual({
...defaultTheme,
container: newTheme.container,
emoji: {
...defaultTheme.emoji,
selected: newTheme.emoji.selected,
},
search: {
...defaultTheme.search,
text: undefined,
},
})
})
})
30 changes: 16 additions & 14 deletions src/utils/deepMerge.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
export type RecursivePartial<T> = Partial<{
[P in keyof T]: T[P] extends object ? Partial<T[P]> : T[P]
[P in keyof T]: T[P] extends Record<string, any> ? RecursivePartial<T[P]> : T[P]
}>

const objectKeys = <T extends object>(obj: T) => Object.keys(obj) as (keyof T)[]

export const deepMerge = <T extends object>(source: T, additional: RecursivePartial<T>): T => {
const result = { ...source }
objectKeys(additional).forEach((key) => {
if (key && additional[key] && typeof additional[key] === 'object') {
result[key] = deepMerge(
source[key],
// @ts-ignore
additional[key],
) as unknown as T[typeof key]
export const deepMerge = <T extends Record<K, any>, K extends keyof T>(
source: T,
additional: RecursivePartial<T>,
): T => {
const result: T = { ...source }
;(Object.keys(additional) as K[]).forEach((key) => {
if (
key in source &&
key in additional &&
typeof source[key] === 'object' &&
typeof additional[key] === 'object'
) {
result[key] = deepMerge(source[key], additional[key] as RecursivePartial<T[K]>)
} else {
// @ts-ignore
result[key] = additional[key]
if (typeof source[key] === 'object' && additional[key] === undefined) return
result[key] = additional[key] as T[K]
}
})
return result
Expand Down

0 comments on commit c17f39c

Please sign in to comment.