Skip to content

Commit

Permalink
Add populate() util method
Browse files Browse the repository at this point in the history
  • Loading branch information
aedart committed Feb 21, 2024
1 parent 91219e7 commit 8f3e9f7
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/support/src/objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './isCloneable';
export * from './isset';
export * from './merge';
export * from './merger';
export * from './populate';
export * from './set';
export * from './uniqueId';

Expand Down
53 changes: 53 additions & 0 deletions packages/support/src/objects/populate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { isKeySafe } from "@aedart/support/reflections";

/**
* Populate target object with the properties from source object
*
* **Warning**: _This method performs a shallow copy of properties in source object!_
*
* **Warning**: _`target` object is mutated!_
*
* **Note**: _Properties that are [unsafe]{@link import('@aedart/support/reflections').isKeyUnsafe} are always disregarded!_
*
* @param {object} target
* @param {object} source
* @param {PropertyKey | PropertyKey[]} [keys='*'] If wildcard (`*`) given, then all properties from the `source` are selected.
* @param {boolean} [safe=true] When `true`, properties must exist in target (_must be defined in target_),
* before they are shallow copied.
*
* @returns {object} The populated target
*
* @throws {TypeError} If a key does not exist in `target` (_when `safe = true`_).
* Or, if key does not exist in `source` (_regardless of `safe` flag_).
*/
export function populate(target: object, source: object, keys: PropertyKey|PropertyKey[] = '*', safe: boolean = true): object
{
if (keys === '*') {
keys = Reflect.ownKeys(source);
}

if (!Array.isArray(keys)) {
keys = [ keys ];
}

// Always remove dangerous keys
keys = keys.filter((key: PropertyKey) => isKeySafe(key));

// Populate...
for (const key of keys) {
// If "safe" is enabled, then only keys that are already defined in target are allowed.
if (safe && !Reflect.has(target, key)) {
throw new TypeError(`Key "${key.toString()}" does not exist in target object`);
}

// However, fail if property does not exist in source, regardless of "safe" flag.
if (!Reflect.has(source, key)) {
throw new TypeError(`Key "${key.toString()}" does not exist in source object`);
}

// @ts-expect-error At this point, all should be safe...
target[key] = source[key];
}

return target;
}

0 comments on commit 8f3e9f7

Please sign in to comment.