如何使用Typescript继承淘汰赛类?

时间:2014-12-01 14:44:57

标签: knockout.js typescript knockout-2.0

我使用了敲击3.x和Typescript,我想创建一个继承自KnockoutObservableArray<T>的强类型集合类,这样我的类型可以是一个可观察的数组,但也有自己的方法。所以我尝试了这个:

export class Emails implements KnockoutObservableArray<Email>
{
    //add a new email to the list
    addItem() : void
    {
        var email = new ContactEmail();
        ...
        this.push(email);
    };
}

但是这给了我以下错误:

Class Emails declares interface KnockoutObservableArray<Email> but does not implement it:
Type 'Emails' is missing property 'extend' from type 'KnockoutObservableArray<Email>'.

如果我尝试添加extend方法,那么它需要我实现peek方法,依此类推,建议我需要实现所有KnockoutObservableArray。但是我不想重新实现它,我想扩展它,因为KnockoutObservableArray是一个界面而不是一个类,我没有看到任何方法来做到这一点。有办法吗?

4 个答案:

答案 0 :(得分:2)

在不久的将来,这也是困扰我的事情。

为什么?

很简单,有两个原因。

的第一个原因是我从对象的角度来看并不是真正理解JavaScript。对我而言,它一直是一种&#34;脚本语言&#34;就是这样,我听到别人告诉我,但我(仍然是)是一个C#人,所以我基本上忽略了所说的内容,因为它看起来不像C#对象。

所有改变,当我开始使用Type Script时,TS以JS的形式向我展示JS输出的对象.J。

第二个原因是JS的灵活性,更重要的是淘汰赛。

我喜欢简单地只是在C#代码中更改我的JSON端点,而不是在前端进行任何更改,而不是对我的HTML进行一些调整。

如果我想在输出对象中添加一个新字段,那么我可以,然后只需向表中添加一个新列,绑定正确的字段名称和完成的工作。

太棒了。

确实是这样,直到我开始被要求提供基于行的功能。它开始很简单,例如能够删除行并执行内联编辑等操作。

这导致了许多奇怪的冒险,例如:

Knockout JS + Bootstrap + Icons + html binding

和此:

Synchronizing a button class with an option control using knockoutjs

但是这些请求变得越来越陌生,越来越复杂,我开始以疯狂的方式遍历DOM,从我所在的那一行开始,一直到父母,然后回到我的兄弟姐妹,然后把文本扯掉从相邻的元素和其他疯狂。

然后我看到了光

我开始与一个只知道和/或生活在JS领域的初级开发人员合作开展一个项目,他只是问我一个问题。

&#34;如果这一切都给你造成了太大的痛苦,那么为什么不为你的行创建一个视图模型?&#34;

所以下面的代码诞生了。

var DirectoryEntryViewModel = (function ()
{
  function DirectoryEntryViewModel(inputRecord, parent)
  {
    this.Pkid = ko.observable(0);
    this.Name = ko.observable('');
    this.TelephoneNumber = ko.observable('');
    this.myParent = parent;

    ko.mapping.fromJS(inputRecord, {}, this);

  }

  DirectoryEntryViewModel.prototype.SomePublicFunction = function ()
  {
    // This is a function that will be available on every row in the array
    // You can call public functions in the base VM using "myParent.parentFunc(blah)"
    // you can pass this row to that function in the parent using  "myParent.someFunc(this)"
    // and the parent can simply do "this.array.pop(passedItem)" or simillar
  }

  return DirectoryEntryViewModel;
})();

var IndexViewModel = (function ()
{
  function IndexViewModel(targetElement)
  {
    this.loadComplete = ko.observable(false);
    this.phoneDirectory = ko.observableArray([]);
    this.showAlert = ko.computed(function ()
    {
      if (!this.loadComplete())
        return false;
      if (this.phoneDirectory().length < 1)
      {
        return true;
      }
      return false;
    }, this);

    this.showTable = ko.computed(function ()
    {
      if (!this.loadComplete())
        return false;
      if (this.phoneDirectory().length > 0)
      {
        return true;
      }
      return false;
    }, this);

    ko.applyBindings(this, targetElement);
    $.support.cors = true;
  }

  IndexViewModel.prototype.Load = function ()
  {
    var _this = this;
    $.getJSON("/module3/directory", function (data)
    {
      if (data.length > 0)
      {
        _this.phoneDirectory(ko.utils.arrayMap(data, function (item)
        {
          return new DirectoryEntryViewModel(item, _this);
        }));
      } else
      {
        _this.phoneDirectory([]);
      }
      _this.loadComplete(true);
    });
  };

  return IndexViewModel;
})();

window.onload = function ()
{
  var pageView = document.getElementById('directoryList');
  myIndexViewModel = new IndexViewModel(pageView);
  myIndexViewModel.Load();
};

现在这不是想象中任何一个最好的例子(我只是把它从我不得不提出的项目中拉出来),但它确实有效。

是的,您必须确保如果在JSON中向后端添加字段,您还要将其添加到行视图模型(可以使用RequireJS或类似版本加载),以及添加该列在需要的地方/你的表/列表/其他标记。

但是为了额外的一两行,你可以轻松地添加一个函数,然后在集合的每一行都可用。

而且,就打字稿而言,上面的整个JS都是由TS编译器从实现相同模式的TS源生成的。

我有很多东西以这种方式运行(我的github页面上有几个例子 - http://github.com/shawty你可以克隆),我运行的一些应用程序有完整的视图模型基于计算的observable中的单个简单更改来调整整个UI,我有行管理它们自己的状态(包括直接与数据库交谈),然后在操作成功后更新它们在父表中的状态。

希望这会给你更多的思考。虽然你可以在你的路上摔跤,试图扩展现有的KO课程,但我认为你会发现上述模式更容易掌握。

我尝试过和你一样的方法,但是一旦我的JS朋友指出为行创建一个VM并重新使用它是多么容易,我就相当快地放弃了它。

希望它有所帮助 美女

答案 1 :(得分:2)

您实际需要的是extends KnockoutObservableArray而不是implements类的类。

然而,扩展需要实现而不是接口,并且这种实现在Typescript中不可用。只有一个静态界面KnockoutObservableArrayStatic 返回一个实现,完全类似于淘汰赛的ko.observableArray实施。

我用favoring composition over inheritance解决了我的类似案例。例如:

export class Emails
{
    collection = ko.observableArray<Email>();

    //add a new email to the list
    addItem() : void
    {
        var email = new ContactEmail();
        ...
        this.collection.push(email);
    };
}

您可以访问集合属性以绑定到DOM,或者如果您需要observableArray的任何功能:

<div data-bind="foreach: myEmails.collection">
...
</div>

答案 2 :(得分:1)

implements关键字用于表示您将实现提供的接口...为了满足这个条件,您需要实现以下接口中定义的所有方法(因为您需要从KnockoutObservableArray<T>开始关注整个链。

或者,创建一个较小的界面来表示您在自定义类中实际需要的内容,如果它是所有这些的子集,您将能够将您的界面用于自定义类以及正常类KnockoutObservableArray<T>个类(将在结构上通过接口测试)。

interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
    extend(requestedExtenders: { [key: string]: any; }): KnockoutObservableArray<T>;
}

interface KnockoutObservable<T> extends KnockoutSubscribable<T>, KnockoutObservableFunctions<T> {
    (): T;
    (value: T): void;

    peek(): T;
    valueHasMutated?:{(): void;};
    valueWillMutate?:{(): void;};
    extend(requestedExtenders: { [key: string]: any; }): KnockoutObservable<T>;
}

interface KnockoutObservableArrayFunctions<T> {
    // General Array functions
    indexOf(searchElement: T, fromIndex?: number): number;
    slice(start: number, end?: number): T[];
    splice(start: number): T[];
    splice(start: number, deleteCount: number, ...items: T[]): T[];
    pop(): T;
    push(...items: T[]): void;
    shift(): T;
    unshift(...items: T[]): number;
    reverse(): T[];
    sort(): void;
    sort(compareFunction: (left: T, right: T) => number): void;

    // Ko specific
    replace(oldItem: T, newItem: T): void;

    remove(item: T): T[];
    remove(removeFunction: (item: T) => boolean): T[];
    removeAll(items: T[]): T[];
    removeAll(): T[];

    destroy(item: T): void;
    destroy(destroyFunction: (item: T) => boolean): void;
    destroyAll(items: T[]): void;
    destroyAll(): void;
}

interface KnockoutSubscribable<T> extends KnockoutSubscribableFunctions<T> {
    subscribe(callback: (newValue: T) => void, target?: any, event?: string): KnockoutSubscription;
    subscribe<TEvent>(callback: (newValue: TEvent) => void, target: any, event: string): KnockoutSubscription;
    extend(requestedExtenders: { [key: string]: any; }): KnockoutSubscribable<T>;
    getSubscriptionsCount(): number;
}

interface KnockoutSubscribableFunctions<T> {
    notifySubscribers(valueToWrite?: T, event?: string): void;
}

interface KnockoutObservableFunctions<T> {
    equalityComparer(a: any, b: any): boolean;
}

以下是替代示例...假设您需要pushremove作为示例。 (旁注:继承是不可能的,因为这些都是接口 - 如果Knockout支持继承,可以更改类型定义以允许免工作extends而不是努力工作implements这个答案的版本......)

这个例子中有趣的是第一个界面 - 它是你定义的一个自定义界面,但是匹配它的任何东西都可以,包括KnockoutObservableArray<Email>。这是一个结构类型的例子,可以节省你一些时间。结构打字要你去酒吧。

/// <reference path="scripts/typings/knockout/knockout.d.ts" />

interface MyObservableArray<T> {
    push(...items: T[]): void;
    remove(item: T): T[];
}

class Email {
    user: string;
    domain: string;
}

class ObservableEmailArray implements MyObservableArray<Email> {
    push(...items: Email[]): void {

    }

    remove(item: Email): Email[] {
        return [];
    }
}

var observableArrayOne: KnockoutObservableArray<Email> = ko.observableArray<Email>();
var observableArrayTwo: MyObservableArray<Email> = new ObservableEmailArray();

function example(input: MyObservableArray<Email>) {
    // Just an example - will accept input of type MyObservableArray<Email>...
}

example(observableArrayOne);

example(observableArrayTwo);

答案 3 :(得分:0)

我遇到了同样的问题并找到了解决方法。

   export class ShoppingCartVM {
        private _items: KnockoutObservableArray<ShoppingCartVM.ItemVM>;
        constructor() {
            this._items = ko.observableArray([]);
            var self = this;
            (<any>self._items).Add = function() { self.Add(); }
            return <any>self._items;
        }

        public Add() {
            this._items.push(new ShoppingCartVM.ItemVM({ Product: "Shoes", UnitPrice: 1, Quantity: 2 }));
        }
    }

您无法将ShoppingCartVM转换为KnockoutObservableArray,但您可以引入像Steve Fenton回答的常见界面。