背景
在阅读官方文档的时候,遇到了如下代码。
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,而DigitalClock和AnalogClock这两个类都是由ClockInterface这一接口继承来的,并没有实现ClockConstructor,按照我的理解,这样的代码很显然是不合法的。但这段代码无疑又是正确的,毕竟这是官方示例,又能编译运行通过。
对于Interface的理解
既然是官方的示例,还是要从官方文档入手理解。在官方文档对Interface的介绍中第一句就写到:“One of TypeScript’s core principles is that type-checking focuses on the shape that values have. ”,也就是说,TypeScript的核心原则是对值的“形状”进行检查。这里的值的形状就是指他所拥有的属性和方法。
分析createClock的第一个参数,ctor: ClockConstructor,在TypeScript,他的意思是第一个参数要和ClockConstructor这一Interface的形状一样。什么叫形状一样?就是要有相同的属性和方法。那ClockConstructor有且只有一个构造方法即new (hour: number, minute: number): ClockInterface(我得吐槽一下Interface的构造方法为啥不也用constructor啊看起来好不自然啊。)。也就是说,只要你提供一个构造器接受两个数字作为参数,同时构造完成一个符合ClockInterface形状的对象,那就是符合要求的。
如果这一想法是正确的,那如下的代码应该也能够工作:
class someClock {
constructor(a: number, b: number) {}
tick() {}
}
let testClock = createClock(someClock, 8, 12)
someClock并未声明implements ClockInterface,但也能被createClock所接受,这也证明了之前的想法,他的要求是参数列表和返回值类型能对的上,达到这个要求就可以被接受。
总结
会产生这样的问题,还是因为把其他语言的思路往TypeScript上套了。例如在JAVA中,如果采用类似的实现,那么只有显式声明了implements ClockInterface的类才能被creatClock方法所接受。但解决问题还是要从官方文档入手,理解了“TypeScript的核心原则之一是对值所具有的结构进行类型检查”这句话,才能更好地学习TypeScript。