网站搜索

typescript - 从混合接口类型创建类


首先,我对 TS 非常陌生,在我阅读 http://www.typescriptlang.org/Handbook 时,我停在试图理解混合类型接口如何工作的地方。

在TS的例子中:

interface ICounter {
    (start: number): string;
    interval: number;
    reset(): void;
}
var c: ICounter;
c(10);
c.reset();
c.interval = 5.0;

所以,问题出在当我尝试使用这个接口编写一个类时,问题出在这一行:

(start: number): string;

起初我以为这条线代表一个函数,所以尝试创建:

class Test implements ICounter {
    interval: number;

    reset(): void { }

    start(start: number): string {
        return "";
    }
}

但这一直显示“Types Test 和 ICounter 拥有不兼容的签名”,所以我这里漏掉了什么?我以为接口对类和变量应该是一样工作的。

答案#1

ICounter 表示一个具有两个属性的函数——interval 和 reset。

界面中的这一行...

(start: number): string;

...描述了一种调用该函数的方法。在示例中显示为:

c(10)

其他行描述了该函数的属性——interval 和 reset。

c.reset();
c.interval = 5.0;

如手册中所述,这用于表示执行此操作的 JavaScript 库。例如,上述内容可以表示以下 JavaScript 代码:

function counter(start) {
    alert(start);
    return "some string";
}

counter.reset = function() { alert('reset called'); };
counter.interval = 1;

除此之外,请注意,您描述的类可以用以下接口表示:

interface ITest {
    interval: number;
    reset: () => void;
    start: (start: number) => string;
}

答案#2

class 意味着你将通过 new 运算符创建实例。new 创建的是 object,而不是 function,因此不可能实现实例可以作为函数调用的类。你可以这样做:

interface ICounterObject {
    interval: number;
    reset(): void;
}

interface ICounter extends ICounterObject {
    (start: number): string;
    __proto__: ICounterObject;
}

class Test implements ICounterObject {
    static createCounter(): ICounter {
        var counter = <ICounter>function (start: number): string {
            return "";
        };
        counter.__proto__ = Test.prototype;
        return counter;
    }

    interval: number;

    reset(): void { }
}

注意,__proto__ 来自 ES6,但实际上在 ES5 浏览器中也受到支持。如果你想使用函数的原型成员(例如 call 和 apply),可以混合原型:

function mixWithFunc<T extends Function>(obj: { __proto__?}, func: T) {
    var objProto = <{ constructor }>obj.__proto__;
    var objClass = <{ __mixedProto__ }>objProto.constructor;
    var proto = <typeof obj>objClass.__mixedProto__;
    if (!proto) {
        proto = {};
        proto.__proto__ = objProto;
        ['call', 'apply', 'bind'].forEach(p => proto[p] = Function.prototype[p]);
        objClass.__mixedProto__ = proto;
    }
    (<typeof obj>func).__proto__ = proto;
    Object.getOwnPropertyNames(obj).forEach(p => func[p] = obj[p]);
    return func;
}

interface ICounter extends Counter {
    (start: number): string;
}

class Counter {
    static create() {
        var self: ICounter = mixWithFunc(
            new Counter(),
            <ICounter>function (start: number) {
                return "started with interval: " + self.interval;
            });
        return self;
    }

    interval = 1000;

    reset() { this.interval = 0; }
}

当然,你可以直接向函数的每个实例添加成员,而不是使用原型。