Skip to content

Commit 1a42224

Browse files
authored
feat: create defuSchema
Fixes unjs#48 Minor code improvement by using optional chaining as well when checking for merger function
1 parent e0e2644 commit 1a42224

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

src/defu.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { Merger, DefuFn as DefuFunction, DefuInstance } from "./types";
1+
import type {
2+
Merger,
3+
DefuFn as DefuFunction,
4+
DefuInstance,
5+
DefuFnSchema,
6+
} from "./types";
27

38
function isObject(value: any) {
49
return value !== null && typeof value === "object";
@@ -28,7 +33,7 @@ function _defu<T>(
2833
continue;
2934
}
3035

31-
if (merger && merger(object, key, value, namespace)) {
36+
if (merger?.(object, key, value, namespace)) {
3237
continue;
3338
}
3439

@@ -79,4 +84,16 @@ export const defuArrayFn = createDefu((object, key, currentValue) => {
7984
}
8085
});
8186

87+
// Custom version for skipping keys not present in defaults
88+
export const defuSchema = createDefu((schema, key) => {
89+
// Checking for hasOwnProperty is required for compatibility with older browsers
90+
if (
91+
!Object.hasOwn?.(schema, key) ||
92+
!Object.prototype.hasOwnProperty.call(schema, key)
93+
) {
94+
return true;
95+
}
96+
return false;
97+
}) as DefuFnSchema;
98+
8299
export type { Defu } from "./types";

src/types.ts

+18
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ export type DefuFn = <
5656
...defaults: Defaults
5757
) => Defu<Source, Defaults>;
5858

59+
export type DefuFnSchema = <
60+
Source extends Input,
61+
Defaults extends Array<Input | IgnoredInput>
62+
>(
63+
source: Source,
64+
...defaults: Defaults
65+
) => Defaults extends [infer F, ...infer Rest]
66+
? F extends Input
67+
? Rest extends Array<Input | IgnoredInput>
68+
? Defu<MergeObjects<F, F>, Rest>
69+
: MergeObjects<F, F>
70+
: F extends IgnoredInput
71+
? Rest extends Array<Input | IgnoredInput>
72+
? Defu<F, Rest>
73+
: F
74+
: F
75+
: never;
76+
5977
export interface DefuInstance {
6078
<Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
6179
source: Source | IgnoredInput,

test/defu.test.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expectTypeOf } from "expect-type";
22
import { it, describe, expect } from "vitest";
3-
import { defu, createDefu, defuFn, defuArrayFn } from "../src/defu";
3+
import { defu, createDefu, defuFn, defuArrayFn, defuSchema } from "../src/defu";
44

55
// Part of tests brought from jonschlinkert/defaults-deep (MIT)
66
const nonObject = [null, undefined, [], false, true, 123];
@@ -22,6 +22,25 @@ describe("defu", () => {
2222
expectTypeOf(result2).toEqualTypeOf<{ a: string; d: string }>();
2323
});
2424

25+
it("defuSchema(): should skip keys non present in default object", () => {
26+
const result = defuSchema(
27+
{ a: 1, b: 2, c: 3, d: 4 },
28+
{ a: 2, c: 4, d: 6, e: 8 }
29+
);
30+
expect(result).toEqual({
31+
a: 1,
32+
c: 3,
33+
d: 4,
34+
e: 8,
35+
});
36+
expectTypeOf(result).toEqualTypeOf<{
37+
a: number;
38+
c: number;
39+
d: number;
40+
e: number;
41+
}>();
42+
});
43+
2544
it("should copy nested values", () => {
2645
const result = defu({ a: { b: "c" } }, { a: { d: "e" } });
2746
expect(result).toEqual({

0 commit comments

Comments
 (0)