From 4cd8a05180c6f141fd88aa9ef088668e4dc94067 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Thu, 1 Sep 2016 23:04:09 +0200 Subject: [PATCH] Added error message when endless recursion is nigh --- src/core/computedvalue.ts | 14 +++++++++++--- test/observables.js | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/core/computedvalue.ts b/src/core/computedvalue.ts index a32f2b634..4a9e08bb6 100644 --- a/src/core/computedvalue.ts +++ b/src/core/computedvalue.ts @@ -46,6 +46,7 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva protected value: T = undefined; name: string; isComputing: boolean = false; // to check for cycles + isRunningSetter: boolean = false; // TODO optimize, see: https://reaktor.com/blog/javascript-performance-fundamentals-make-bluebird-fast/ setter: (value: T) => void; /** @@ -129,10 +130,17 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva } public set(value: T) { - if (this.setter) - this.setter.call(this.scope, value); + if (this.setter) { + invariant(!this.isRunningSetter, `The setter of computed value '${this.name}' is trying to update itself. Did you intend to update an _observable_ value, instead of the computed property?`); + this.isRunningSetter = true; + try { + this.setter.call(this.scope, value); + } finally { + this.isRunningSetter = false; + } + } else - throw new Error(`[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`); + invariant(false, `[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`); } private trackAndCompute(): boolean { diff --git a/test/observables.js b/test/observables.js index 2aa5d43fd..65fad621e 100644 --- a/test/observables.js +++ b/test/observables.js @@ -1876,3 +1876,19 @@ test('computed getter / setter for plan objects should succeed', function (t) { t.end(); }); + +test('helpful error for self referencing setter', function(t) { + var a = observable({ + x: 1, + get y() { + return this.x + }, + set y(v) { + this.y = v // woops...;-) + } + }) + + t.throws(() => a.y = 2, /The setter of computed value/) + + t.end() +})