@synet/patterns
Version:
Robust, battle-tested collection of stable patterns used in Synet packages
92 lines (91 loc) • 2.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValueObject = void 0;
/**
* Base class for Value Objects - immutable objects defined by their property values.
*
* Value Objects encapsulate domain concepts that have no distinct identity.
* Two Value Objects are equal when all their properties are equal.
*
* @see /docs/value-object.md for detailed documentation and examples
*
* @template T - The shape of properties that define this Value Object
* @example
* class Email extends ValueObject<{ address: string }> {
* private constructor(props: { address: string }) {
* super(props);
* }
*
* public static create(address: string): Result<Email> {
* if (!address.includes('@')) {
* return Result.fail('Email must contain @ symbol');
* }
* return Result.success(new Email({ address }));
* }
*
* get value(): string {
* return this.props.address;
* }
* }
*/
class ValueObject {
/**
* Creates a new Value Object with the given properties.
* The properties are frozen to ensure immutability.
*/
constructor(props) {
this.props = Object.freeze({ ...props });
}
/**
* Checks if this Value Object is equal to another Value Object.
* Two Value Objects are considered equal if all their properties are equal.
*/
equals(vo) {
if (vo === null || vo === undefined) {
return false;
}
if (!(vo instanceof this.constructor)) {
return false;
}
return this.isEqual(this.props, vo.props);
}
/**
* Deep comparison of properties.
* This handles nested objects and arrays properly.
*/
isEqual(obj1, obj2) {
if (obj1 === obj2) {
return true;
}
if (typeof obj1 !== "object" ||
obj1 === null ||
typeof obj2 !== "object" ||
obj2 === null) {
return obj1 === obj2;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
return keys1.every((key) => {
// Type assertion needed because TypeScript doesn't recognize keys1/keys2 are keys of obj1/obj2
return (keys2.includes(key) &&
this.isEqual(obj1[key], obj2[key]));
});
}
/**
* Returns a string representation of this Value Object.
*/
toString() {
return JSON.stringify(this.props);
}
/**
* Returns a shallow copy of the props.
* This can be used when you need to access the raw data.
*/
toObject() {
return { ...this.props };
}
}
exports.ValueObject = ValueObject;