|
| 1 | +import { expect } from 'tstyche' |
| 2 | +import { deepmerge, type DeepMergeFn, type DeepMergeAllFn, DeepMergeDefinedFn, DeepMergeAllDefinedFn } from '.' |
| 3 | + |
| 4 | +expect(deepmerge()).type.toBeAssignableTo<Function>() |
| 5 | +expect(deepmerge()).type.toBe<DeepMergeFn>() |
| 6 | + |
| 7 | +expect(deepmerge()({ a: 'a' }, { b: 'b' }).a).type.toBe<string>() |
| 8 | +expect(deepmerge()({ a: 'a' }, { b: 'b' }).b).type.toBe<string>() |
| 9 | +expect(deepmerge()({ a: 2 }, { b: 'b' }).a).type.toBe<number>() |
| 10 | +expect(deepmerge()({ a: 2 }, { b: 'b' }).b).type.toBe<string>() |
| 11 | +expect(deepmerge()({ a: 2 }, { a: 'b' }).a).type.toBe<string>() |
| 12 | + |
| 13 | +expect(deepmerge).type.not.toBeCallableWith(1) |
| 14 | +expect(deepmerge).type.not.toBeCallableWith({ symbols: 2 }) |
| 15 | +expect(deepmerge).type.not.toBeCallableWith({ symbol: 2 }) |
| 16 | + |
| 17 | +expect(deepmerge({ symbols: true })).type.toBeAssignableTo<Function>() |
| 18 | +expect(deepmerge({ symbols: true })).type.toBe<DeepMergeFn>() |
| 19 | + |
| 20 | +expect(deepmerge()('string', { a: 'string' }).a).type.toBe<string>() |
| 21 | +expect(deepmerge()(1, { a: 'string' }).a).type.toBe<string>() |
| 22 | + |
| 23 | +expect(deepmerge()<Object, string>({ a: 'string' }, 'string')).type.toBe<string>() |
| 24 | +expect(deepmerge()<Object, number>({ a: 'string' }, 1)).type.toBe<number>() |
| 25 | +expect(deepmerge()({ a: 'string' }, new Date())).type.toBe<Date>() |
| 26 | +expect(deepmerge()({ a: 'string' }, /a/g)).type.toBe<RegExp>() |
| 27 | +expect(deepmerge()(/a/, {})).type.toBe<{}>() |
| 28 | + |
| 29 | +expect(deepmerge()({ a: 'string' }, { a: 1 }).a).type.toBe<number>() |
| 30 | +expect(deepmerge()({ a: 'string' }, { b: 1 }).a).type.toBe<string>() |
| 31 | +expect(deepmerge()({ a: 'string' }, { }).a).type.toBe<string>() |
| 32 | +expect(deepmerge()({ a: 'string' }, { b: 1 }).b).type.toBe<number>() |
| 33 | +expect(deepmerge()({ a: { a: 'string' } }, { b: 1 }).a).type.toBe<{ a: string }>() |
| 34 | +expect(deepmerge()({ a: { a: 'string' } }, { a: { a: 1 } }).a).type.toBe<{ a: number }>() |
| 35 | +expect(deepmerge()({ a: { a: 'string' } }, { a: { a: 1, b: 'string' } }).a).type.toBe<{ a: number, b: string }>() |
| 36 | +expect(deepmerge()({ a: { a: { a: 'string' } } }, { a: { a: 1, b: 'string' } }).a).type.toBe<{ a: number, b: string }>() |
| 37 | +expect(deepmerge()({ a: { a: { a: 'string' } } }, { a: { a: { b: 'string' } } }).a).type.toBe<{ a: { a: string, b: string } }>() |
| 38 | +expect(deepmerge()({ a: [1, 2, 3, 4] }, { a: 'string' }).a).type.toBe<string>() |
| 39 | +expect(deepmerge()({ a: [1, 2, 3, 4] }, { a: [1, 2, 3, 4] }).a).type.toBe<number[]>() |
| 40 | +expect(deepmerge()({ a: [1, 2, 3, 4] }, { a: ['a'] }).a).type.toBe<(number | string)[]>() |
| 41 | +expect(deepmerge()({ a: [1, 2, 3, 4] as readonly number[] }, { a: ['a'] }).a).type.toBe<(number | string)[]>() |
| 42 | +expect(deepmerge()({ a: [1, 2, 3, 4] as const }, { a: ['a'] as const }).a).type.toBe<[1, 2, 3, 4, 'a']>() |
| 43 | +expect(deepmerge()({ a: [1] }, { a: [2] }).a).type.toBe<Array<number>>() |
| 44 | +expect(deepmerge()({ a: { b: {} } }, { a: { b: [2] } }).a).type.toBe<{ b: number[] }>() |
| 45 | +expect(deepmerge()({ a: { b: {} } }, { a: { b: new Date() } }).a).type.toBe<{ b: Date }>() |
| 46 | +expect(deepmerge()({ a: { b: {} } }, { a: { b: /abc/g } }).a).type.toBe<{ b: RegExp }>() |
| 47 | +expect(deepmerge()({ a: { b: {} } }, new Date())).type.toBe<Date>() |
| 48 | +expect(deepmerge()({ a: { b: {} } }, new Map())).type.toBe<Map<any, any>>() |
| 49 | + |
| 50 | +expect(deepmerge({ all: true })).type.toBeAssignableTo<Function>() |
| 51 | +expect(deepmerge({ all: true })).type.toBe<DeepMergeAllFn>() |
| 52 | +expect(deepmerge({ all: true, symbols: true })).type.toBeAssignableTo<Function>() |
| 53 | +expect(deepmerge({ all: true, symbols: true })).type.toBe<DeepMergeAllFn>() |
| 54 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }).a).type.toBe<string>() |
| 55 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }, { b: 'a' }).a).type.toBe<string>() |
| 56 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }, { b: 'a' }).b).type.toBe<string>() |
| 57 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }, { a: 2 }).a).type.toBe<number>() |
| 58 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }, 2)).type.toBe<number>() |
| 59 | +expect(deepmerge({ all: true, symbols: true })({ a: 'a' }, 'string')).type.toBe<string>() |
| 60 | + |
| 61 | +expect(deepmerge).type.not.toBeCallableWith({ mergeArray: function () {} }) |
| 62 | +expect(deepmerge).type.not.toBeCallableWith({ |
| 63 | + mergeArray: function () { |
| 64 | + return () => 'test' |
| 65 | + } |
| 66 | +}) |
| 67 | + |
| 68 | +deepmerge({ |
| 69 | + mergeArray: function (options) { |
| 70 | + expect(options.clone).type.toBe<(value: any) => any>() |
| 71 | + const clone = options.clone |
| 72 | + return function (target, source) { |
| 73 | + return clone(target.concat(source)) |
| 74 | + } |
| 75 | + } |
| 76 | +}) |
| 77 | +deepmerge({ |
| 78 | + mergeArray: function () { |
| 79 | + return function (_target, source) { |
| 80 | + return source |
| 81 | + } |
| 82 | + } |
| 83 | +}) |
| 84 | +deepmerge({ |
| 85 | + isMergeableObject: function (value) { |
| 86 | + return true |
| 87 | + } |
| 88 | +}) |
| 89 | + |
| 90 | +expect(deepmerge.isMergeableObject).type.toBe<(value: any) => boolean>() |
| 91 | + |
| 92 | +expect(deepmerge({ onlyDefinedProperties: true })).type.toBe<DeepMergeDefinedFn>() |
| 93 | +expect(deepmerge({ all: true, onlyDefinedProperties: true })).type.toBe<DeepMergeAllDefinedFn>() |
| 94 | + |
| 95 | +interface FullOptions { |
| 96 | + apiUrl: string |
| 97 | + timeout: number |
| 98 | + nested: { |
| 99 | + enabled: boolean |
| 100 | + value: string |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +type PartialOptions = Partial<FullOptions> |
| 105 | + |
| 106 | +const fullTarget: FullOptions = { apiUrl: 'url', timeout: 100, nested: { enabled: true, value: 'test' } } |
| 107 | +const partialSource: PartialOptions = { timeout: 200 } |
| 108 | + |
| 109 | +const mergedDefined = deepmerge({ onlyDefinedProperties: true })(fullTarget, partialSource) |
| 110 | +expect(mergedDefined.apiUrl).type.toBe<string>() |
| 111 | +expect(mergedDefined.timeout).type.toBe<number>() |
| 112 | +expect(mergedDefined.nested).type.toBe<{ enabled: boolean; value: string }>() |
| 113 | + |
| 114 | +expect(mergedDefined).type.toBeAssignableTo<FullOptions>() |
| 115 | + |
| 116 | +// eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 117 | +const mergedRegular = deepmerge()(fullTarget, partialSource) |
| 118 | + |
| 119 | +type DeepPartialOptions = { |
| 120 | + apiUrl?: string |
| 121 | + timeout?: number |
| 122 | + nested?: { |
| 123 | + enabled?: boolean |
| 124 | + value?: string |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +const deepPartialSource: DeepPartialOptions = { nested: { enabled: false } } |
| 129 | +const mergedDeep = deepmerge({ onlyDefinedProperties: true })(fullTarget, deepPartialSource) |
| 130 | +expect(mergedDeep.apiUrl).type.toBe<string>() |
| 131 | +expect(mergedDeep.timeout).type.toBe<number>() |
| 132 | +expect(mergedDeep.nested).type.toBeAssignableTo<{ enabled: boolean; value: string }>() |
| 133 | + |
| 134 | +const mergedAll = deepmerge({ all: true, onlyDefinedProperties: true })( |
| 135 | + fullTarget, |
| 136 | + { timeout: 300 } as PartialOptions, |
| 137 | + { apiUrl: 'newUrl' } as PartialOptions |
| 138 | +) |
| 139 | +expect(mergedAll).type.toBeAssignableTo<FullOptions>() |
0 commit comments