Tom HANAX
Tom HANAX

Reputation: 432

Computed is not assignable to Observable after upgrade to Knockout.js 3.5.0

Knockout.js 3.5.0 comes with it's own type definitions on npm. Let's have this variable x defined as:

const x: ko.Observable<boolean> = ko.computed(() => true);

Now we got compiler error: Type 'Computed' is missing the following properties from type 'Observable': valueHasMutated, valueWillMutate.

I understand what compiler is trying to tell me, but I think that principally this is wrong - because every computed is also an observable. This behaviour worked well on 3.4.x using type definitions from @types/knockout.

There is the Subscribable type, which I believe is some "parent" type, and it works in declaration:

const x: ko.Subscribable<boolean> = ko.computed(() => true);

This throws no error. However no type checking is performed on value setter:

x(2);    // this is wrong but no compiler error

Can anybody shed some light on these new type hierarchy changes and how should we use it?

Upvotes: 2

Views: 1373

Answers (5)

ProLoGic
ProLoGic

Reputation: 1

I use a combination of an Observable and a Computed as work-around.

InEditMode: Observable<boolean> = ko.observable(false);
InEditModeComputed: Computed<void> = ko.computed(() => {
    const result = this.ComposeMode() === EComposeMode.Edit && !this.SaveConsultatieInProgress();
    this.InEditMode(result);
});

Upvotes: 0

Jon Keeping
Jon Keeping

Reputation: 59

In Knockout 3.5.1, I was able to reference the interface PureComputed defined in the Knockout package:

import * as ko from 'knockout';
import { Computed, PureComputed, Observable } from 'knockout';

class MyClass
  originalComments: string;
  comments: Observable<string>;

  constructor(serverData: any) {
    this.originalComments = serverData.comments || '';
    this.comments = ko.observable(this.originalComments);
  }

  hasChanged: PureComputed<boolean> = ko.pureComputed(() => this.comments() !==  this.originalComments);
}

Upvotes: 1

Greg Veres
Greg Veres

Reputation: 1890

I am going through the upgrade to 3.5.0 myself right now. It is a big change since we should be using the type file that comes with the project and that changes all the names of the classes and gets rid of the global ko. I think these are all good things. I have been happy with the transformation of my code (well, except for trying to get knockout-validation to compile).

I ran into the exact issue you are running into. I knew that when I had assigned a computed to an observable typed variable that it was a hack, but I also knew it would work just fine because the interface was close enough. This upgrade forced me to revisit the hack and to deal with it properly. Here is how I do it now in my code:

I know define the variable as one that takes either an observable or a computed:

class SomeClass {
   public value: Observable<boolean> | Computed<boolean>;
}

This allows all the other developers who might be reading the code that value might be a computed so they shouldn't just blindly assign a value to it.

Upvotes: 1

codevision
codevision

Reputation: 5520

Alternatively you can add in your compiler options

"path" {
   ...
   "knockout": [ "node_modules/@types/knockout" ],
   ...
}

that way you can use old KO typings until better way to upgrade code would be found

Upvotes: 1

adiga
adiga

Reputation: 35202

You can use KnockoutComputed<T> like this:

const x: KnockoutComputed<boolean> = ko.computed(() => true);

Upvotes: 0

Related Questions