【问题标题】:Extending Object.prototype with TypeScript使用 TypeScript 扩展 Object.prototype
【发布时间】:2013-05-24 16:01:50
【问题描述】:

我目前正在开发一个 TypeScript API,它需要一些附加功能绑定到对象原型 (Object.prototype)。

考虑以下代码:

class Foo {

}

interface Object {
    GetFoo(): Foo;
    GetFooAsString(): string;
}

//This is problematic...
Object.prototype.GetFoo = function() {
    return new Foo();
    // Note, this line is just for testing...I don't want my function to just return a blank instance of Foo!
}

//This is ok.
Object.prototype.GetFooAsString = function () {
    return this.GetFoo().toString();
}

您可能想直接在Playground 尝试此操作。

如您所见,我有一个名为Foo 的类(不是我将使用的实际对象名称)。我还扩展了Object 接口以包含两个新功能。最后,我实现了针对prototype 的函数(这些函数在纯 JavaScript 中工作,只是 TypeScript 会抱怨)。

我在哪里注释了“//这是有问题的......”TypeScript 用红色波浪线突出显示,并显示以下错误:

Cannot convert '() => Foo' to '{ (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; }': Call signatures of types '() => Foo' and '{ (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; (): Foo; }' are incompatible
() => Foo

要么这只是一个 TypeScript 错误(我知道它仍处于开发阶段,所以很多错误需要解决,我已经在 CodePlex 上说明了其中的一些),或者我遗漏了一些东西。

为什么我会遇到这个问题?

如果不是 TypeScript 错误,我该如何解决?

【问题讨论】:

    标签: javascript object interface prototype typescript


    【解决方案1】:

    此错误已在 TS 0.9.0 alpha 中修复,如下所示:

    游乐场仍在运行 0.8.3。

    这基本上是因为一些关键接口(对象、数字、字符串)等上的方法被缓存作为性能优化。

    如果你运行这个。第一次加载时,您不会看到该错误。 Try It

    一旦您对该代码进行了编辑,解析器就会再次检查该代码,并且由于它缓存了旧接口定义,因此会看到重复的函数定义,然后有效地崩溃了。您对该文件进行的编辑越多,错误语句就会变得越复杂。

    【讨论】:

    • 太棒了!很高兴这不是因为编码不好……至少不是我的 ;-)
    【解决方案2】:

    我曾经有:

    // See if an array contains an object
    Array.prototype.contains = function (obj) {
        var i = this.length;
        while (i--) {
            if (this[i] === obj) {
                return true;
            }
        }
        return false;
    }
    

    为了使用 typescript 编译该代码,我添加了以下行:

    interface Array {
        contains(obj: Object): boolean;
    }
    

    谢谢basarat

    【讨论】:

    • 不会包含(obj:任何):布尔值;在这里更合适?
    • 注意:我必须在这里使用interface Array<T>,否则我会得到“错误 TS2428:接口的所有声明必须具有相同的类型参数。”
    • 如何使用import
    【解决方案3】:

    我以同样的方式扩展了数组,当一个聚会使用for i in ... 循环它时遇到了一个大问题。现在您无法控制所有第三方代码,这些错误可能会非常烦人,因此我建议使用更好的方法:

    interface Array<T> {
       crandom(): T;
    }
    
    /** Retrieve a random element from the list */
     Object.defineProperty(Array.prototype, 'crandom', { value: function() {
    
        let index = Math.floor(Math.random() * this.length);
    
        return this[index];
    }
    });
    

    现在通过使用Object.defineProperty,您的新属性将不会被枚举,而且它是安全的。上面的代码几乎给出了数组中的一个随机元素。我也做了另一个,它从数组中弹出一个随机元素:

    Object.defineProperty(Array.prototype, 'popRandom', { value: function() {
    
        let index = Math.floor(Math.random() * this.length);
    
        let result = this[index];
        this.splice(index, 1);
    
        return result;
    }
    });
    

    使用Object.defineProperty,您可以更好地控制此创建,还可以添加其他限制。

    【讨论】:

    • 正是如此。它允许您控制其枚举和不变性,但是在社区中不赞成和不鼓励在现有对象(如数组、字符串、数字等)上添加或覆盖功能。您可能会发现您的代码在使用 defineProperty 和 writable: false 时有效,因为第 3 方代码无法覆盖您的代码
    • 好点,我不同意它被皱眉,因为在功能应该存在但不存在的情况下扩展基类可能非常好(例如数组上的random 方法)。只要您不编写供他人使用的库,我个人认为扩展基础没有问题,因为它被适度使用并且不会过度使用到地狱。
    • 当 ECMAScript 委员会批准标准以在这些对象上实现新功能时会发生什么,但您已经添加了具有相同名称和不同行为的自己的功能?突然之间,您的库因不合规而死机。 PrototypeJS 就是一个著名的例子
    • 正是我提到以这种方式创建库不好的原因,我用 crandom 之类的前缀命名我的方法,而不是 random
    • 只是不要在任何需要随机性强加密或重要资源(例如真钱)处于危险的情况下使用Math.random()Math.random() 是可利用的,并且在密码学上不强。见this answer for alternatives。在在线洗牌游戏或任何攻击者可以利用预测下一个值的能力来实现不公平或恶意结果的环境中使用它都是一个坏主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 2012-01-10
    • 2018-10-09
    • 2018-04-08
    • 1970-01-01
    • 2018-07-09
    • 2016-10-23
    相关资源
    最近更新 更多