【问题标题】:Create Class From Hybrid Interface Type从混合接口类型创建类
【发布时间】:2015-08-14 18:10:16
【问题描述】:

首先,我对 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 "";
    }
}

但这一直显示“类型测试和 ICounter 具有不兼容的签名”,所以我在这里缺少什么?我认为接口应该以相同的方式处理类和变量。

【问题讨论】:

    标签: interface typescript


    【解决方案1】:

    ICounter 表示一个具有两个属性的函数——intervalreset

    界面中的这一行……​​

    (start: number): string;
    

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

    c(10)
    

    其他行描述了函数的属性——intervalreset

    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;
    }
    

    【讨论】:

    • tks David,这解决了我的疑问(我认为),我最初认为接口应该始终由任何东西实现。
    【解决方案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 浏览器实际上支持。如果你想使用函数原型成员(例如callapply)混合原型:

    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; }
    }
    

    当然,您可以只向函数的每个实例添加成员,而不是进行原型设计。

    【讨论】:

    • 我将 David 标记为答案,但您的解释很棒,但我只能投票支持 1 个解决方案,感谢 @Artem!我不太了解 es6(或打字稿),但我会进一步研究以完全理解您的第二部分答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多