Skip to content

Commit 3eb3985

Browse files
feat(mergeProps): add mergeProps function (#740)
* feat: mergeProps function * test: add unit tests for mergeProps function * refactor: tsx to ts * Update src/mergeProps.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * docs: add JSDoc comment to mergeProps function explaining behavior with undefined values --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 2a5333e commit 3eb3985

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export { default as set, merge, mergeWith } from './utils/set';
77
export { default as warning, noteOnce } from './warning';
88
export { default as omit } from './omit';
99
export { default as toArray } from './Children/toArray';
10+
export { default as mergeProps } from './mergeProps';

src/mergeProps.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Merges multiple props objects into one. Unlike `Object.assign()` or `{ ...a, ...b }`, it skips
3+
* properties whose value is explicitly set to `undefined`.
4+
*/
5+
function mergeProps<A, B>(a: A, b: B): B & A;
6+
function mergeProps<A, B, C>(a: A, b: B, c: C): C & B & A;
7+
function mergeProps<A, B, C, D>(a: A, b: B, c: C, d: D): D & C & B & A;
8+
function mergeProps(...items: any[]) {
9+
const ret: any = {};
10+
for (const item of items) {
11+
if (item) {
12+
for (const key of Object.keys(item)) {
13+
if (item[key] !== undefined) {
14+
ret[key] = item[key];
15+
}
16+
}
17+
}
18+
}
19+
return ret;
20+
}
21+
22+
export default mergeProps;

tests/mergeProps.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import mergeProps from '../src/mergeProps';
2+
3+
describe('mergeProps', () => {
4+
it('merges two objects with later overriding earlier', () => {
5+
const a = { foo: 1, bar: 2 };
6+
const b = { bar: 3, baz: 4 };
7+
expect(mergeProps(a, b)).toEqual({ foo: 1, bar: 3, baz: 4 });
8+
});
9+
10+
it('excludes keys with undefined values', () => {
11+
const a = { foo: 1, bar: undefined };
12+
const b = { bar: 2 };
13+
expect(mergeProps(a, b)).toEqual({ foo: 1, bar: 2 });
14+
});
15+
16+
it('does not include key if value is undefined in last object', () => {
17+
const a = { foo: 1 };
18+
const b = { bar: undefined };
19+
expect(mergeProps(a, b)).toEqual({ foo: 1 });
20+
});
21+
22+
it('skips null and undefined items', () => {
23+
const a = { foo: 1 };
24+
expect(mergeProps(a, null as any)).toEqual({ foo: 1 });
25+
expect(mergeProps(a, undefined as any)).toEqual({ foo: 1 });
26+
expect(mergeProps(null as any, a)).toEqual({ foo: 1 });
27+
expect(mergeProps(undefined as any, a)).toEqual({ foo: 1 });
28+
});
29+
30+
it('merges three or more objects with rightmost winning', () => {
31+
const a = { a: 1 };
32+
const b = { a: 2, b: 2 };
33+
const c = { a: 3, b: 3, c: 3 };
34+
expect(mergeProps(a, b, c)).toEqual({ a: 3, b: 3, c: 3 });
35+
});
36+
37+
it('returns empty object for no args', () => {
38+
expect((mergeProps as (...items: any[]) => any)()).toEqual({});
39+
});
40+
41+
it('returns copy of single object', () => {
42+
const a = { foo: 1, bar: 2 };
43+
expect((mergeProps as (...items: any[]) => any)(a)).toEqual({
44+
foo: 1,
45+
bar: 2,
46+
});
47+
});
48+
49+
it('handles empty objects', () => {
50+
expect(mergeProps({}, { a: 1 }, {})).toEqual({ a: 1 });
51+
});
52+
});

0 commit comments

Comments
 (0)