@memoized built-in decorator
January 22, 2026 ยท View on GitHub
The @memoized decorator memoizes (caches) the result of a function or getter and returns the cached result on subsequent accesses.
This can be useful both for lazy evaluation and memoization of expensive computations.
class C {
@memoized accessor foo {
get () {
return expensiveComputation();
}
}
@memoized get bar () {
return expensiveComputation2();
}
// Memoized results are keyed by the arguments
@memoized baz (a, b, c) {
return expensiveComputation3(a, b, c);
}
}
It applies to the following kinds of decorators:
accessorgetmethodclass
Later, it can also apply to these decorator extensions:
letpropertyfunction
Using it on any other kind of decorator will throw a TypeError.
Why not @lazy?
Note that decorators cannot be used to implement the common pattern of replacing a getter with a value property when accessed, so this seems to be the only path forwards for lazy evaluation.
Implementation sketch
This is meant to be informative, and not a complete polyfill. In this implementation, memoized constructors or methods depend on the composites proposal for convenience and simplicity.
Note
Using a Symbol property to store the old value for readability. In practice, the decorators would not add any observable properties to the object.
const fnCache = new Map();
function memoized (value, { kind, name }) {
if (kind === "accessor" || kind === "get") {
let getter = kind === "accessor" ? value.get : value;
let key = new Symbol(name);
let get = function() {
// Or should it be Object.hasOwn(this, key) ?
if (key in this) {
// Already computed
return this[key];
}
// Compute and cache
return this[key] = getter.call(this);
}
return kind === "accessor" ? Object.assign(value, { get }) : get;
}
if (kind === "method") {
// Memoize keyed by arguments
let fn = value;
return function(...args) {
let key = Composite([fn, ...args]);
if (fnCache.has(key)) {
return fnCache.get(key);
}
let result = fn.apply(this, args);
fnCache.set(key, result);
return result;
}
}
if (kind === "class") {
// Memoize keyed by arguments
let fn = value;
return class extends value {
constructor(...args) {
let key = Composite([fn, ...args]);
if (fnCache.has(key)) {
return fnCache.get(key);
}
super(...args);
fnCache.set(key, this);
}
}
}
throw new TypeError(`@memoized cannot be used on ${kind} constructs`);
}