logo hsb.horse
← Back to snippets index

Snippets

Lazy-Initialized Object

A TypeScript pattern using getters to generate values only on first access. Lightweight lazy evaluation compared to memory caching.

Published: Updated:

There are cases where you want to execute heavy initialization only on first access, then reuse the generated value thereafter. This pattern achieves this simply, without the overhead of a memory cache.

Code

export interface Reusable<T> {
value: T;
}
export function reusable<T>(init: () => T): Reusable<T> {
return {
get value() {
const value = init();
Object.defineProperty(this, "value", { value });
return value;
},
};
}

Usage Examples

Fixed Timestamp

const timestamp = reusable(() => new Date());
// Date is generated on first access
console.log(timestamp.value); // 2025-01-18T09:00:00.000Z
// Same value returned on subsequent accesses
console.log(timestamp.value); // 2025-01-18T09:00:00.000Z

Heavy Configuration Loading

const config = reusable(() => {
// File loading, environment variable parsing, etc.
return loadConfigFromFile();
});
// Configuration is loaded only when needed
function processData() {
const settings = config.value; // Loading executes only on first access
// ...
}

How It Works

  1. Executes the init function on first getter access
  2. Overwrites value as a data property using Object.defineProperty
  3. On subsequent accesses, the getter is not called and the value is returned directly

This ensures initialization cost only occurs on first access, with performance equivalent to regular property access thereafter.

Comparison with Memory Caching

Differences from memory caches (like Map):

  • This pattern is specialized for single values
  • No key management like Map required
  • Garbage collection behaves the same as regular objects

Conversely, use Map or WeakMap when you want to cache multiple values.

Caveats

Once initialized, the value never changes. To reinitialize, you must create a new reusable instance. Also, if the init function throws an exception, the same exception will be re-thrown on subsequent accesses.