【问题标题】:Understanding Constructor Interfaces in typescript理解打字稿中的构造函数接口
【发布时间】:2016-09-14 12:32:11
【问题描述】:

我是 typescript 的新手,我一直在理解构造函数接口以及它们的类型检查方式。这是来自文档的 sn-p:

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

以下是文档对上述代码的说明:

因为createClock的第一个参数是ClockConstructor类型,在 createClock(AnalogClock, 7, 32),它检查 AnalogClock 是否有 正确的构造函数签名。

现在这实质上意味着 DigitalClock 类或 AnalogClock 类具有由 ClockConstructor 接口定义的类型。如何?它是一个类,接口描述了一个构造函数。

请问有没有人?

【问题讨论】:

    标签: javascript interface typescript


    【解决方案1】:

    让我们从示例中的简单界面开始:

    interface ClockInterface {
        tick();
    }
    

    这个接口定义了这个类型的一个实例包含tick方法,这两个实现了这个接口:

    class MyClock implements ClockInterface {
        public tick(): void {
            console.log("tick");
        }
    }
    
    let a: ClockInterface = new MyClock();
    let b: ClockInterface = {
        tick: () => console.log("tick")
    }
    

    这很简单,因为类实现与其他 OO 语言中的相同,第二个实现并不简单,但对于 javascript 开发人员来说应该很容易理解。

    这很好用!但是如果我想获得一个类的构造函数作为我的函数的参数会发生什么?
    这行不通:

    function constructorClock(ctor: ClockInterface): ClockInterface {
        return new ctor();
    }
    

    这里的参数是ClockInterface 的实例而不是类(/构造函数),所以为了处理这种情况,我们可以为类本身定义一个接口而不是实例:

    interface ClockConstructor {
        new (hour: number, minute: number): ClockInterface;
    }
    

    现在我们可以有这个功能了:

    function constructorClock(ctor: ClockConstructor): ClockInterface {
        return new ctor(3, 5);
    }
    

    这些构建器接口给我们的另一个问题是定义静态类成员/方法的能力,一个很好的例子是ArrayConstructor(它是lib.d.ts的一部分):

    interface ArrayConstructor {
        new (arrayLength?: number): any[];
        new <T>(arrayLength: number): T[];
        new <T>(...items: T[]): T[];
        (arrayLength?: number): any[];
        <T>(arrayLength: number): T[];
        <T>(...items: T[]): T[];
        isArray(arg: any): arg is Array<any>;
        prototype: Array<any>;
    }
    

    (定义取决于您使用的是ES5 还是ES6 目标,这是ES5 之一)。

    如您所见,接口定义了不同的构造函数签名,prototypeisArray 函数,您可以像这样使用它:

    Array.isArray([1,2])
    

    如果您没有能力为类本身(而不是实例)提供接口,那么您将无法使用此 isArray 函数,因为这是错误的:

    let a = [];
    a.isArray(3);
    

    DigitalClockAnalogClock 类通过tick 方法实现ClockInterface(即它们的实例 具有此方法),但它们实现了ClockConstructor 接口他们的constructor 函数与new 一起使用,它返回一个ClockInterface 的实例。

    希望这有助于澄清它


    编辑

    构造函数当然不会返回interface,它会返回一个实现了ClockInterface接口的实例。
    也许这会让事情变得更容易:

    class BaseClock {
        protected hour: number;
        protected minute: number;
    
        constructor(hour: number, minute: number) {
            this.hour = hour;
            this.minute = minute;
        }
    
        public tick() {
            console.log(`time is: ${ this.hour }:${ this.minute }`);
        }
    }
    
    class DigitalClock extends BaseClock {
        constructor(hour: number, minute: number) {
            super(hour, minute);
        }
    
        tick() {
            console.log("digitial");
            super.tick();
        }
    }
    
    class AnalogClock extends BaseClock {
        constructor(hour: number, minute: number) {
            super(hour, minute);
        }
    
        tick() {
            console.log("analog");
            super.tick();
        }
    }
    
    interface ClockConstructor {
        new (hour: number, minute: number): BaseClock;
    }
    
    function createClock(ctor: ClockConstructor, hour: number, minute: number): BaseClock {
        return new ctor(hour, minute);
    }
    

    我们现在只使用类而不是接口,这样更有意义吗?

    语法:new (hour: number, minute: number): ClockInterface 定义了一个构造函数,this:

    function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
        return new ctor(hour, minute);
    }
    
    createClock(DigitalClock, 12, 17);
    

    就像:

    function createDigitalClock(hour: number, minute: number): ClockInterface {
        return new DigitalClock(hour, minute);
    }
    
    createDigitalClock(12, 17);
    

    new ctor(hour, minute);(其中ctorClockConstructor)类似于new DigitalClock(hour, minute)(只是更通用)。

    【讨论】:

    • 好吧,我的问题是:接口 ClockConstructor 如何表示类类型?也许我不明白语法: new (hour: number, minute: number): ClockInterface;作为 ClockInterface 的返回类型如何有意义?
    • 检查我修改后的答案
    • 是的,基类示例很有意义。我对此仍然有点困惑:新(小时:数字,分钟:数字):ClockInterface。我是否应该将其作为一种语法来定义实现某些构造函数的类的接口?
    • 所有的 typescript 类都实现了一个构造函数接口,因为所有的类实例都是像new ClassName(...params?: any[]) 这样创建的。所以是的,构造函数类型(类)的语法是:{ new (...params?: any[]): ClassOrInterfaceType },即:function create(ctor: { new (x: number, y: number): Point }) { ... }
    猜你喜欢
    • 2017-11-25
    • 1970-01-01
    • 2020-03-08
    • 2013-01-26
    • 2022-01-23
    • 2018-04-08
    • 1970-01-01
    • 2018-05-22
    相关资源
    最近更新 更多