How does interfaces with construct signatures work?
我在计算如何在接口中定义构造函数时遇到了一些困难。我可能完全误解了什么。但我已经找了很长一段时间的答案,我找不到与此相关的任何信息。
如何在typescript类中实现以下接口:
1 2 3 | interface MyInterface { new ( ... ) : MyInterface; } |
AndersHejlsberg创建了一个界面,其中包含与此类似的内容(大约14分钟)。但对我来说,我不能在课堂上实现这一点。
我可能误解了什么,我没有得到什么?
编辑:
澄清。带有"new(…)"我的意思是"任何事"。我的问题是我连最基本的版本都得不到:
1 2 3 4 5 6 7 | interface MyInterface { new () : MyInterface; } class test implements MyInterface { constructor () { } } |
号
这不是为我编译。我得到"class'test'声明接口'myinterface',但没有实现它:类型'myinterface'需要构造签名,但类型'test'在试图编译它时缺少一个。
编辑:
所以在研究了这一点之后,给出了更多的反馈。
1 2 3 4 5 6 7 | interface MyInterface { new () : MyInterface; } class test implements MyInterface { constructor () => test { return this; } } |
不是有效的typescript,这无法解决问题。不能定义构造函数的返回类型。它将返回"测试"。以下签字:等级测试{施工方()}似乎是"new()=>test"(通过将鼠标悬停在在线编辑器中的"class"上,只粘贴该代码即可获得)。这就是我们想要的,我想是的。
有人能提供一个这样或类似的例子吗?在实际编译的地方?
编辑(再次…):
因此,我可能想出了一个想法,为什么可以在接口中定义它,但不可能在typescript类中实现它。以下是可行的:
1 2 3 4 5 6 7 8 9 10 11 12 | var MyClass = (function () { function MyClass() { } return MyClass; })(); interface MyInterface { new () : MyInterface; } var testFunction = (foo: MyInterface) : void => { } var bar = new MyClass(); testFunction(bar); |
。
那么,这仅仅是一个让你可以连接javascript的typescript特性吗?或者可以在不使用javascript实现类的情况下用typescript实现它吗?
接口中的构造签名不能在类中实现;它们只用于定义现有的JSAPI,这些API定义了一个"新的"可实现的函数。下面是一个涉及接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | interface ComesFromString { name: string; } interface StringConstructable { new(n: string): ComesFromString; } class MadeFromString implements ComesFromString { constructor (public name: string) { console.log('ctor invoked'); } } function makeObj(n: StringConstructable) { return new n('hello!'); } console.log(makeObj(MadeFromString).name); |
这为您可以用以下方法调用
1 2 3 4 5 6 | class Other implements ComesFromString { constructor (public name: string, count: number) { } } makeObj(Other); // Error! Other's constructor doesn't match StringConstructable |
在我寻找完全相同的问题时,我去寻找排版团队是如何做到这一点的…
它们声明一个接口,然后声明一个名称与接口名称完全匹配的变量。这也是输入静态函数的方法。
来自
1 2 3 4 5 6 7 8 9 10 11 | interface Object { toString(): string; toLocaleString(): string; // ... rest ... } declare var Object: { new (value?: any): Object; (): any; (value: any): any; // ... rest ... } |
我试过了,很有魅力。
与构造签名的接口并不意味着由任何类来实现(乍一看,对于像我这样的C/Java背景的人来说,这可能看起来很奇怪,但给它一个机会)。这有点不同。
暂时把它想象成一个带有调用签名的接口(就像Java世界中的一个"函数接口")。它的目的是描述一个函数类型..类。所描述的签名应该由函数对象来满足……但不只是任何高级函数或方法。它应该是一个知道如何构造对象的函数,一个在使用
因此,带有构造签名的接口定义了构造函数的签名!类的构造函数应符合接口中定义的签名(将其视为构造函数实现接口)。就像建筑工人或工厂!
下面是一小段代码,试图演示最常见的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | interface ClassicInterface { // old school interface like in C#/Java method1(); ... methodN(); } interface Builder { //knows how to construct an object // NOTE: pay attention to the return type new (myNumberParam: number, myStringParam: string): ClassicInterface } class MyImplementation implements ClassicInterface { // The constructor looks like the signature described in Builder constructor(num: number, s: string) { } // obviously returns an instance of ClassicInterface method1() {} ... methodN() {} } class MyOtherImplementation implements ClassicInterface { // The constructor looks like the signature described in Builder constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface method1() {} ... methodN() {} } // And here is the polymorphism of construction function instantiateClassicInterface(ctor: Builder, myNumberParam: number, myStringParam: string): ClassicInterface { return new ctor(myNumberParam, myStringParam); } // And this is how we do it let iWantTheFirstImpl = instantiateClassicInterface(MyImplementation, 3.14,"smile"); let iWantTheSecondImpl = instantiateClassicInterface(MyOtherImplementation, 42,"vafli"); |
从设计的角度来看,通常不需要在接口中指定构造函数需求。接口应该描述可以在对象上执行的操作。如果需要的话,应该允许实现接口的不同类需要不同的构造函数参数。
例如,如果我有一个接口:
1 2 3 4 | interface ISimplePersistence { load(id: number) : string; save(id: number, data: string): void; } |
我可能有将数据存储为cookie(不需要构造函数参数)的实现,以及将数据存储在数据库(需要连接字符串作为构造函数参数)中的版本。
如果您仍然想在接口中定义构造函数,有一种肮脏的方法可以做到这一点,我曾经回答过这个问题:
带有构造签名的接口不是类型检查
为了达到预期的行为,你可以使用装饰,即使这可能不是他们应该使用的。
这个
1 2 3 4 5 6 7 8 9 10 11 12 | interface MyInterface { new (); } function MyInterfaceDecorator(constructor: MyInterface) { } @MyInterfaceDecorator class TestClass { constructor () { } } |
编译时没有问题。相反,下面的testclass定义
1 2 3 4 5 | // error TS2345: Argument of type 'typeof TestClass' is not assignable to parameter of type 'MyInterface'. @MyInterfaceDecorator class TestClass { constructor (arg: string) { } } |
不会编译。