diff --git a/README.md b/README.md index c41f7b2..211b725 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,19 @@ defuArrayFn({ **Note:** the function is called only if the value defined in defaults is an aray. +## Schema object +You can use `defuSchema` if you want to remove keys that are not present in the default object. + +```js +console.log( + defuSchema( + { a: 1, b: 2, c: 3, d: 4 }, + { a: 2, c: 4, d: 6, e: 8 } + ) +); +// => { a: 1, c: 3, d: 4, e: 8 } +`` + ### Remarks - `object` and `defaults` are not modified diff --git a/lib/defu.cjs b/lib/defu.cjs index 5212929..a7dab83 100644 --- a/lib/defu.cjs +++ b/lib/defu.cjs @@ -1,4 +1,4 @@ -const { defu, createDefu, defuFn, defuArrayFn } = require('../dist/defu.cjs'); +const { defu, createDefu, defuFn, defuArrayFn, defuSchema } = require('../dist/defu.cjs'); module.exports = defu; @@ -8,4 +8,4 @@ module.exports.default = defu; module.exports.createDefu = createDefu; module.exports.defuFn = defuFn; module.exports.defuArrayFn = defuArrayFn; - +module.exports.defuSchema = defuSchema; diff --git a/src/defu.ts b/src/defu.ts index 271c023..533f41d 100644 --- a/src/defu.ts +++ b/src/defu.ts @@ -1,5 +1,10 @@ import { isPlainObject } from "./_utils"; -import type { Merger, DefuFn as DefuFunction, DefuInstance } from "./types"; +import type { + Merger, + DefuFn as DefuFunction, + DefuInstance, + DefuFnSchema, +} from "./types"; // Base function to apply defaults function _defu( @@ -73,4 +78,9 @@ export const defuArrayFn = createDefu((object, key, currentValue) => { } }); +// Custom version for skipping keys not present in defaults +export const defuSchema = createDefu((schema, key) => { + return !(key in schema); +}) as DefuFnSchema; + export type { Defu } from "./types"; diff --git a/src/types.ts b/src/types.ts index d4dd3d5..473a433 100644 --- a/src/types.ts +++ b/src/types.ts @@ -56,6 +56,24 @@ export type DefuFn = < ...defaults: Defaults ) => Defu; +export type DefuFnSchema = < + Source extends Input, + Defaults extends Array, +>( + source: Source, + ...defaults: Defaults +) => Defaults extends [infer F, ...infer Rest] + ? F extends Input + ? Rest extends Array + ? Defu, Rest> + : MergeObjects + : F extends IgnoredInput + ? Rest extends Array + ? Defu + : F + : F + : never; + export interface DefuInstance { >( source: Source | IgnoredInput, diff --git a/test/defu.test.ts b/test/defu.test.ts index c2f1600..b51624b 100644 --- a/test/defu.test.ts +++ b/test/defu.test.ts @@ -1,6 +1,6 @@ import { expectTypeOf } from "expect-type"; import { it, describe, expect } from "vitest"; -import { defu, createDefu, defuFn, defuArrayFn } from "../src/defu"; +import { defu, createDefu, defuFn, defuArrayFn, defuSchema } from "../src/defu"; import * as asteriskImport from "./fixtures/"; // Part of tests brought from jonschlinkert/defaults-deep (MIT) @@ -23,6 +23,25 @@ describe("defu", () => { expectTypeOf(result2).toMatchTypeOf<{ a: string; d: string }>(); }); + it("defuSchema(): should skip keys non present in default object", () => { + const result = defuSchema( + { a: 1, b: 2, c: 3, d: 4 }, + { a: 2, c: 4, d: 6, e: 8 }, + ); + expect(result).toEqual({ + a: 1, + c: 3, + d: 4, + e: 8, + }); + expectTypeOf(result).toEqualTypeOf<{ + a: number; + c: number; + d: number; + e: number; + }>(); + }); + it("should copy nested values", () => { const result = defu({ a: { b: "c" } }, { a: { d: "e" } }); expect(result).toEqual({