【问题标题】:How should I add a typescript definition for a knockout extender which adds methods to an observable?我应该如何为将方法添加到可观察对象的淘汰扩展程序添加打字稿定义?
【发布时间】:2017-07-04 10:40:26
【问题描述】:

我有一个想要在打字稿文件中使用的淘汰扩展器,但我不知道如何声明它。

扩展器是用 javascript 编写的,我想从 typescript 文件中使用它(因为该项目目前是 javascript 和 typescript 的混合体)。但是,我无法弄清楚如何为扩展器添加定义。从this question 我已经添加到KnockoutExtenders 界面(虽然我不确定我是否错过了这个问题的重点),如下所示:

interface KnockoutExtenders {
    trackChange<T>(target: T, track: boolean): ITrackChange<T>;
}

interface ITrackChange<T> extends KnockoutObservable<T> {
    setOriginalValue(startingValue: T): void;
    resetToConfirmedValue(): void;
    setConfirmedValue(): void;
    numberChanges(): number;
}

扩展器的用法如下:

class ProductTypeViewModel {
    constructor() {
        this.name.setOriginalValue('Initial value');
    }

    name = ko.observable('').extend({
        trackChange: true
    });
}

我在this.name.setOriginalValue 上收到打字稿构建错误,因为Property 'setOriginalValue' does not exist on type 'KnockoutObservable&lt;string&gt;',这当然是真的。

我应该如何声明扩展器,以便 typescript 可以识别 observable 已被赋予新方法(即它不再是 KnockoutObservable&lt;string&gt; 而现在是 ITrackChange&lt;string&gt;)?

(不确定扩展器的解释是否相关,但无论如何: 扩展器跟踪对可观察对象的更改并允许它们被接受/取消。这允许使用 observable 来显示当前值,并且在编辑模式上,相同的 observable 可用于更改值,但在用户确认模式中的更改之前,底层值实际上不会更改。如果他们取消它们,实际上什么都没有改变。)

【问题讨论】:

    标签: typescript knockout.js observable


    【解决方案1】:

    试试下面的

    interface KnockoutObservable<T> extends Observable<T> {
      setOriginalValue(param: string): void;
    }
    
    class ProductTypeViewModel {
        private name: KnockoutObservable<string>;
    
        constructor() {
            this.name.setOriginalValue('Initial value');
        }
    }
    

    【讨论】:

    • 这种方法的问题在于它会告诉 typescript 编译器所有的 observables 都有 setOriginalValue 方法,但实际上并非如此。仅当使用 .extend({ trackChange: true }) 扩展 observable 时才添加该方法。
    • 我不确定这是否可以在编译时完成。我会考虑将该逻辑移到方法中。
    【解决方案2】:

    我已经能够通过手动转换 observable 的类型来绕过打字稿错误。

    name = ko.observable('').extend({
        trackChange: true
    }) as ITrackChange<string>;
    

    我希望能够在某个地方注册它,这样我就不必在我扩展的每个 observable 上都使用这个转换,但是手动完成就足够了。如果有人提出更好的解决方案,我会很乐意接受他们的回答。 (如果这是一种糟糕的方法,请发表评论以解释原因,我总是很乐意学习!)

    请注意,如果 observable 有多个扩展器,则可以使用交集类型来显示所有相关接口,如 this answer to a different question

    name = ko.observable(false).extend({
        trackChange: true,
        logChange: true
    }) as ITrackChange<boolean> & ILogChange<boolean>;
    

    【讨论】:

    • 您也可以通过索引器访问属性来绕过它,例如 "this.name["setOriginalValue"]('Initial value')"
    • 感谢您的建议,但这对我不起作用。 this.name['setOriginalValue'] 的返回类型为 KnockoutBindingHandler,尝试将其作为函数调用时会出现错误 Value of type 'KnockoutBindingHandler' is not callable。我也收到错误Cannot invoke an expression whose type lacks a call signature. (TS2349)
    • @Tim 我需要在此处显示的类型脚本中实现所需的扩展器 knockoutjs.com/documentation/extenders.html 。我是打字稿的新手,你能提出建议吗??
    • @SrinivasRa 如果您是 typescript 的新手,我建议您先完成一个快速的 typescript 教程以掌握该语言的窍门,然后再回到您的淘汰赛扩展程序。这些 cmets 不是获得帮助的理想场所,因为没有多少人会看到它们,而且它们确实适用于与此答案相关的 cmets,但如果您遇到困难,您可以随时提出新问题来获得帮助。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 2013-08-11
    • 2013-04-12
    • 1970-01-01
    • 2016-11-09
    • 2013-02-27
    • 1970-01-01
    相关资源
    最近更新 更多