angular/angular-cli · error · Error

Memoize decorator can only be used on methods or get accesso

Error message

Memoize decorator can only be used on methods or get accessors.

What it means

The `memoize` decorator (a TC39 decorator wrapping methods/getters) checks `context.kind` and only supports `'method'` and `'getter'` member kinds. Applying `@memoize` to any other kind (setter, field, or a plain function in a context where kind differs) is rejected at decoration time, since caching semantics only make sense for parameterless-ish calls and accessors.

Source

Thrown at packages/angular/cli/src/utilities/memoize.ts:21

 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.dev/license
 */

/**
 * A decorator that memoizes methods and getters.
 *
 * **Note**: Be cautious where and how to use this decorator as the size of the cache will grow unbounded.
 *
 * @see https://en.wikipedia.org/wiki/Memoization
 */
export function memoize<This, Args extends unknown[], Return>(
  target: (this: This, ...args: Args) => Return,
  context: ClassMemberDecoratorContext,
) {
  if (context.kind !== 'method' && context.kind !== 'getter') {
    throw new Error('Memoize decorator can only be used on methods or get accessors.');
  }

  const cache = new Map<string, Return>();

  return function (this: This, ...args: Args): Return {
    for (const arg of args) {
      if (!isJSONSerializable(arg)) {
        throw new Error(
          `Argument ${isNonPrimitive(arg) ? arg.toString() : arg} is JSON serializable.`,
        );
      }
    }

    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key) as Return;
    }

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Apply `@memoize` only to methods or `get` accessors.
  2. If you need a cached property, convert the field to a `get` accessor: `@memoize get value() {...}`.
  3. Remove the decorator from setters; caching a setter is not meaningful — cache the corresponding getter instead.
  4. If memoizing a free function, wrap it manually with a Map-based cache instead of the decorator.

Example fix

// before
class C {
  @memoize set foo(v: string) { this._v = v; }
}
// after
class C {
  @memoize get foo() { return computeFoo(); }
  set foo(v: string) { this._v = v; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// At class-definition time, only decorate methods/getters:
class C {
  @memoize
  get value(): number { return expensive(); }
}

Type guard

function supportsMemoize(ctx: ClassMemberDecoratorContext): boolean {
  return ctx.kind === 'method' || ctx.kind === 'getter';
}

Try / catch

// Decoration errors surface at class definition; wrap module evaluation if dynamic:
try {
  const mod = await import('./decorated-module.js');
} catch (e) {
  if (e instanceof Error && e.message.includes('Memoize decorator can only be used')) {
    console.error('Fix the @memoize target: use a method or getter.');
  } else { throw e; }
}

Prevention

When it happens

Trigger: Decorating a class setter (`set foo(v)`), a class field, or another unsupported class member with `@memoize`, evaluated when the class is defined.

Common situations: Refactoring a getter into a setter while keeping the decorator; applying the decorator to a property intended as a lazily-computed value (should be a getter instead); copy-pasting the decorator onto the wrong member.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/91e77ac42257d7b5. Report an issue: GitHub.