From 0882cf27bdffdba69b91bc444dd51292973cbc12 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 21 Sep 2026 10:04:54 -0400 Subject: [PATCH] [BUGFIX release]: type overload for one of the new tracked forms was not appropriately allowing the equality options --- packages/@ember/-internals/metal/lib/tracked.ts | 12 ++++++++---- type-tests/@glimmer/tracking-test.ts | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/@ember/-internals/metal/lib/tracked.ts b/packages/@ember/-internals/metal/lib/tracked.ts index 9a9fb9fb1ed..f5dcb079b70 100644 --- a/packages/@ember/-internals/metal/lib/tracked.ts +++ b/packages/@ember/-internals/metal/lib/tracked.ts @@ -121,11 +121,15 @@ interface TrackedDecoratorOptions { description?: string; } +// `PropertyDecorator` covers the `(target, key)` call TypeScript emits for +// native class fields; `ExtendedMethodDecorator` covers the classic-class call. +export type TrackedDecorator = ExtendedMethodDecorator & PropertyDecorator; + /** * `tracked` as a decorator factory: `@tracked({ equals })`, or on classic * classes `tracked({ value })` / `tracked({ initializer })`. */ -export function tracked(propertyDesc: TrackedDecoratorOptions): ExtendedMethodDecorator; +export function tracked(propertyDesc: TrackedDecoratorOptions): TrackedDecorator; /** * `tracked` as a bare decorator: `@tracked foo = 1`. */ @@ -145,7 +149,7 @@ export function tracked( ): TrackedValue; export function tracked( ...args: any[] -): ExtendedMethodDecorator | DecoratorPropertyDescriptor | TrackedValue { +): TrackedDecorator | DecoratorPropertyDescriptor | TrackedValue { assert( `@tracked can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: tracked()`, !(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true) @@ -225,7 +229,7 @@ function isDecoratorOptions(value: unknown): value is TrackedDecoratorOptions { return Object.keys(value).every((key) => DECORATOR_OPTION_KEYS.includes(key)); } -function makeTrackedDecorator(propertyDesc?: TrackedDecoratorOptions): ExtendedMethodDecorator { +function makeTrackedDecorator(propertyDesc?: TrackedDecoratorOptions): TrackedDecorator { if (DEBUG && propertyDesc) { assert( `The options object passed to tracked() may only contain a 'value' or an 'initializer' property, not both. Received: [${Object.keys( @@ -275,7 +279,7 @@ function makeTrackedDecorator(propertyDesc?: TrackedDecoratorOptions): ExtendedM setClassicDecorator(decorator); - return decorator; + return decorator as TrackedDecorator; } if (DEBUG) { diff --git a/type-tests/@glimmer/tracking-test.ts b/type-tests/@glimmer/tracking-test.ts index 87751fe29e1..e2c5bdab9d1 100644 --- a/type-tests/@glimmer/tracking-test.ts +++ b/type-tests/@glimmer/tracking-test.ts @@ -38,9 +38,12 @@ tracked(0, { equals: (a: string, b: string) => a === b }); // ------- decorator forms ------- class Counter { @tracked count = 0; + + @tracked({ equals: Object.is }) another = 0; } expectTypeOf(new Counter().count).toEqualTypeOf(); +expectTypeOf(new Counter().another).toEqualTypeOf(); // classic class form returns a decorator expectTypeOf(tracked({ value: 'Zoey' })).toMatchTypeOf();