Преобразование из Javascript в Typescript, элементы управления Knockout и DevExtreme

У меня возникли некоторые проблемы с преобразованием из Javascript в Typescript и, в частности, с созданием элемента управления devextreme.

В прошлом я бы создал объект в моей модели представления для элементов управления devextreme, используя что-то вроде этого:

self.myButton = {
  text: 'Click Me',
  disabled: ko.purecomputed(function(){ return self.myobservable().length>0;});
}

Что хорошо работает, в машинописном тексте я пробовал...

myButton: DevExpress.ui.dxButtonOptions;

Потом в конструкторе...

....
self.myButton = {
  text: 'Click Me',
  disabled: ko.purecomputed(function(){ return self.myobservable().length>0;});
}
...

Что дает мне ошибку Тип KnockoutComputed не может быть назначен логическому типу, что достаточно справедливо, я понимаю.

Но вопрос в том, как это должно быть сделано?

Я могу просто использовать это:

myButton: any; 

В объявлении параметров кнопки, но я думаю, что это противоречит цели использования машинописного текста ??

Это может сработать, одной из причин перехода к машинописному тексту было также использование таких вещей, как веб-пакет и т. Д., Так что это не полная потеря, я просто хотел бы знать, как это сделать правильно.

Заранее спасибо.


person Jason Coley    schedule 11.07.2017    source источник
comment
Одна идея: вы можете попробовать использовать keyof и расширить интерфейсы DevExpress, чтобы принять KnockoutComputed<T> & T в качестве значения.   -  person unional    schedule 12.07.2017
comment
Как вариант, вы можете использовать knockout-decorator для своих свойств. В этом случае свойства будут иметь простой тип и могут использоваться для опций DevExtreme.   -  person mykhailo.romaniuk    schedule 12.07.2017
comment
@unional Можете ли вы привести какой-нибудь пример того, как использовать keyof с интерфейсом devexpress, я новичок в машинописном тексте, поэтому не знаю, с чего начать?   -  person Jason Coley    schedule 15.07.2017
comment
@mykhailo.romaniuk спасибо за ссылку на это, я никогда не видел этого, но это, безусловно, выглядит хорошо. Однако один вопрос: если бы я это сделал, мне пришлось бы разбить мой pureComputed на отдельный метод виртуальной машины?   -  person Jason Coley    schedule 15.07.2017
comment
@JasonColey Я немного поиграл с knockout-decorators, и кажется, что они не решатся ваша проблема. Также у меня были мысли об использовании knockout-es5, но все решения вашей проблемы с этой библиотекой также выглядит как обходной путь для меня. Я должен сказать, что не вижу лучшего способа, чем использовать any для опций виджета.   -  person mykhailo.romaniuk    schedule 17.07.2017
comment
Я опубликую свои исследования, чтобы вы могли решить сами.   -  person mykhailo.romaniuk    schedule 17.07.2017


Ответы (1)


Я проверил возможности использования knockout-decorators и knockout-es5, чтобы решить вашу проблему. Однако я думаю, что наиболее чистым способом было бы просто использовать any в качестве типа параметров вашего виджета.

Шаблон для обоих примеров одинаков:

<div data-bind="dxButton: buttonOptions"></div>

Использование нокаута-es5

Этот подход требует отдельного создания вычисляемого свойства. Создание этого свойства плохо типизировано, поэтому здесь мы теряем некоторые сильные преимущества типизации.

// importing thing from DevExtreme.
import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";

import * as ko from "knockout";
// importing knockout-es5 to include track and defineProperty functionality
import "knockout-es5";

// This small util will make creation of compute a bit more strong by using keyof
const createComputed = <T>(prototype: T, key: keyof T, computedFunc: Function): void => {
    ko.defineProperty(prototype, key, computedFunc);
}

class DevextremeTestViewModel {
    clickCounter: number = 0;
    buttonOptions: DevExpress.ui.dxButtonOptions;

    constructor() {
        this.buttonOptions = {
            text: "Start",
            onClick: this.increaseCounter
        };

        // start tracking clickCounter property (make it observable)
        ko.track(this, ["clickCounter"]);

        // assign to text property of widget options computed value
        createComputed(this.buttonOptions, "text", () => {
            return `Clicked ${this.clickCounter} times`;
        });
    }

    increaseCounter(): void {
        this.clickCounter++;
    }
}

Использование нокаут-декораторов

Этот подход требует создания вычисляемого свойства отдельно (как часть модели представления). Также требуется «хак» с копированием геттера вычисляемого свойства в параметры виджета:

import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";
// include required decorators
import { observable, computed } from "knockout-decorators";

// Magic function to copy getter from one property to another
const copyGetter = <T, TProp>(prototype: T, key: keyof T, propProto: TProp, propertyKey: keyof TProp) => {
    let getter = Object.getOwnPropertyDescriptor(propProto, propertyKey).get;
    Object.defineProperty(prototype, key, {
        get: getter
    });
}

class DevextremeTestViewModel {
    // Create observable
    @observable clickCounter: number = 0;
    // Create computed that based on observable
    @computed({ pure: true }) get buttonText(): string {
        return `Clicked ${this.clickCounter} times`;
    };
    buttonOptions: DevExpress.ui.dxButtonOptions;

    constructor() {
        this.buttonOptions = {
            text: this.buttonText,
            onClick: this.increaseCounter
        };

        // Need to copy getter from our computed to options property.
        copyGetter(this.buttonOptions, "text", this, "buttonText");
    }

    increaseCounter(): void {
        this.clickCounter++;
    }
}

Использование любого

Как я уже сказал, поскольку мы не можем заставить команду DevExtreme изменить интерфейс опций виджета для поддержки T | KnockoutObservable<T> | KncokoutComputed<T>, самый "чистый" способ для меня - использовать любой:

import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";
import { observable, computed } from "knockout-decorators";
import * as ko from "knockout";

class DevextremeTestViewModel {
    // Create observable
    @observable clickCounter: number = 0;
    buttonOptions: any = {
        text: ko.pureComputed(()=> {
            return `Clicked ${this.clickCounter} times`;
        }),
        onClick: this.increaseCounter
    };

    increaseCounter(): void {
        this.clickCounter++;
    }
}
person mykhailo.romaniuk    schedule 17.07.2017