ValueObject
Base class for primitive value wrappers.
Import
import { ValueObject } from '@haskou/value-objects';Signature
abstract class ValueObject<T extends Primitive = Primitive>Primitive is string | number | boolean | bigint | symbol | null | undefined. Object and array values are intentionally excluded.
Constructor
constructor(value: T | null | undefined)Runtime immutability
The stored value cannot be reassigned, deleted, or redefined, including from JavaScript. Writes in strict mode throw a TypeError; non-strict assignments leave the value unchanged.
Only the stored value is protected. Subclasses can initialize additional fields after super(), and are responsible for protecting those fields if needed. Subclasses must pass their final primitive value to super() instead of redeclaring or assigning value afterward.
NullObject creation
Automatic NullObject creation is enabled by default. A null or undefined constructor value returns a compatible null object instead of storing the nullish value.
Applications that prefer nullish construction to fail immediately can configure the base class once during bootstrap:
ValueObject.disableNullObjectCreation();Nullish construction then throws NullObjectCreationDisabledError. Restore the default behavior with:
ValueObject.enableNullObjectCreation();The setting is global to the process. Explicit NullObject.new(...) creation remains available regardless of this setting.
Object and array values are intentionally excluded from Primitive. Composite domain values should model their fields explicitly instead of inheriting reference equality accidentally. Composite toPrimitives() serialization remains supported independently of this storage constraint.
Equality and value comparison
isEqual() expresses Value Object equality: both objects must have the same concrete class and the same value.
hasValue() deliberately ignores the Value Object class and compares only the wrapped value. It can also compare directly against a primitive.
class UserId extends ValueObject<string> {}
class CommunityId extends ValueObject<string> {}
const userId = new UserId('123');
userId.isEqual(new UserId('123')); // true
userId.isEqual(new CommunityId('123')); // false
userId.isEqual('123'); // false
userId.hasValue(new CommunityId('123')); // true
userId.hasValue('123'); // trueSpecialized Value Objects may normalize value comparison by overriding hasValue(). isEqual() still requires the same concrete class before delegating to that value comparison.
Methods
| Method | Description |
|---|---|
static disableNullObjectCreation() | Makes nullish Value Object construction throw instead of returning a NullObject. |
static enableNullObjectCreation() | Restores automatic NullObject creation. |
valueOf() | Returns the wrapped primitive value. Null objects return undefined. |
toString() | Returns valueOf().toString(). |
hasValue(other) | Compares only the wrapped value; accepts another Value Object or a primitive. |
isEqual(other) | Returns true only for the same concrete Value Object class with an equal value. |
isNotEqual(other) | Negates isEqual(). |
clone(value) | Protected helper used by subclasses to return a new instance of the current class. |
Example
import { ValueObject } from '@haskou/value-objects';
class UserName extends ValueObject<string> {}
const name = new UserName('hasko');
name.valueOf(); // 'hasko'
name.isEqual(new UserName('hasko')); // true
name.hasValue('hasko'); // trueNotes
- Use
isEqual()for domain equality between Value Objects. - Use
hasValue()only when comparing the underlying value is intentional. - Most concrete classes in this package extend
ValueObjectdirectly or indirectly.