Skip to content

Commit

Permalink
feat(utils): defaults
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Jul 25, 2023
1 parent 50a7b99 commit 6da685c
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 6 deletions.
20 changes: 20 additions & 0 deletions src/utils/__tests__/defaults.spec-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @file Type Tests - defaults
* @module tutils/utils/tests/unit-d/defaults
*/

import type Vehicle from '#fixtures/types/vehicle'
import type { Defaults, Partial } from '#src/types'
import type testSubject from '../defaults'

describe('unit-d:utils/defaults', () => {
it('should return Defaults<T, U>', () => {
// Arrange
type T = Partial<Vehicle>
type U = [{ readonly vrm: number }, { readonly vin: string }]
type Expect = Defaults<T, U>

// Expect
expectTypeOf<typeof testSubject<T, U>>().returns.toEqualTypeOf<Expect>()
})
})
27 changes: 27 additions & 0 deletions src/utils/__tests__/defaults.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @file Unit Tests - defaults
* @module tutils/utils/tests/unit/defaults
*/

import type Vehicle from '#fixtures/types/vehicle'
import type { Partial } from '#src/types'
import testSubject from '../defaults'

describe('unit:utils/defaults', () => {
let base: Partial<Vehicle> & { vrm: string }

beforeAll(() => {
base = { vrm: faker.vehicle.vrm() }
})

it('should return merge result', () => {
// Arrange
const s1: { vrm: number } = { vrm: faker.number.int() }
const s2: { vin: string } = { vin: faker.vehicle.vin() }

// Act + Expect
expect(testSubject(base, s1, s2))
.to.eql({ ...base, ...s2 })
.but.not.equal(base)
})
})
6 changes: 4 additions & 2 deletions src/utils/assign-with.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import properties from './properties'
type AssignCustomizer = Fn<[unknown, unknown, string | symbol], unknown>

/**
* Assigns own properties of one or more `source` objects to a cloned `base`
* object. A `customizer` is used to produce assigned values.
* Assigns own properties of one or more `source` objects to a target object.
*
* A `customizer` is used to produce assigned values. The initial `base` object
* **will not** be modified.
*
* Source objects are applied from left to right. Subsequent sources overwrite
* property assignments of previous sources.
Expand Down
4 changes: 2 additions & 2 deletions src/utils/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type { Assign, ObjectCurly, Objectify } from '#src/types'
import assignWith from './assign-with'

/**
* Assigns own properties of one or more `source` objects to a cloned `base`
* object.
* Assigns own properties of one or more `source` objects to a target object.
* The initial `base` object **will not** be modified.
*
* Source objects are applied from left to right. Subsequent sources overwrite
* property assignments of previous sources.
Expand Down
40 changes: 40 additions & 0 deletions src/utils/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file Utilities - defaults
* @module tutils/utils/defaults
*/

import type { Defaults, ObjectCurly, Objectify } from '#src/types'
import assignWith from './assign-with'
import cast from './cast'
import isUndefined from './is-undefined'

/**
* Assigns own properties of one or more `source` objects to a target object for
* all target properties that resolve to `undefined`.
*
* The initial `base` object **will not** be modified.
*
* Source objects are applied from left to right. Subsequent default values are
* ignored if a property no longer resolves to `undefined`.
*
* @see {@linkcode Defaults}
*
* @todo examples
*
* @template T - Base object
* @template U - Source object array
*
* @param {T} base - Base object
* @param {U} source - Source object array
* @return {Defaults<T, U>} Merge result
*/
const defaults = <T extends Objectify<any>, U extends readonly ObjectCurly[]>(
base: T,
...source: U
): Defaults<T, U> => {
return cast(
assignWith((curr, src) => (isUndefined(curr) ? src : curr), base, ...source)
)
}

export default defaults
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export { default as constant } from './constant'
export { default as construct } from './construct'
export { default as count } from './count'
export { default as crush } from './crush'
export { default as defaults } from './defaults'
export { default as define } from './define'
export { default as descriptor } from './descriptor'
export { default as desegment } from './desegment'
Expand Down
2 changes: 1 addition & 1 deletion src/utils/merge-with.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type MergeCustomizer = Fn<[unknown, unknown, string | symbol], unknown>

/**
* Recursively merges own properties of one or more `source` objects into a
* cloned `base` object.
* target object. The initial `base` object **will not** be modified.
*
* A `customizer` is be used to produce merged values. Plain object properties
* are merged recursively. Other objects and value types are overridden by
Expand Down
2 changes: 1 addition & 1 deletion src/utils/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import mergeWith from './merge-with'

/**
* Recursively merges own properties of one or more `source` objects into a
* cloned `base` object.
* target object. The initial `base` object **will not** be modified.
*
* Plain object properties are merged recursively. Other objects and value types
* are overridden by assignment.
Expand Down

0 comments on commit 6da685c

Please sign in to comment.