typescript 具有构造签名的接口如何工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13407036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does interfaces with construct signatures work?
提问by Nypan
I am having some trouble working out how defining constructors in interfaces work. I might be totally misunderstanding something. But I have searched for answers for a good while and I can not find anything related to this.
我在弄清楚接口中定义构造函数的工作方式时遇到了一些麻烦。我可能完全误解了一些东西。但是我已经搜索了很长时间的答案,但找不到与此相关的任何内容。
How do I implement the following interface in a TypeScript class:
如何在 TypeScript 类中实现以下接口:
interface MyInterface {
new ( ... ) : MyInterface;
}
Anders Hejlsberg creates an interface containing something similar to this in this video(at around 14 minutes). But for the life of me I can not implement this in a class.
Anders Hejlsberg 创建了一个界面,其中包含与此视频中的内容类似的内容(大约 14 分钟)。但是对于我的生活,我无法在课堂上实现这一点。
I am probably misunderstanding something, what am I not getting?
我可能误解了一些东西,我没有得到什么?
EDIT:
编辑:
To clarify. With "new ( ... )" I meant "anything". My problem is that I can not get even the most basic version of this working:
澄清。“新(...)”我的意思是“任何东西”。我的问题是,我什至无法获得此工作的最基本版本:
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () { }
}
This is not compiling for me I get "Class 'test' declares interface 'MyInterface' but does not implement it: Type 'MyInterface' requires a construct signature, but Type 'test' lacks one" when trying to compile it.
这不是为我编译我得到“类'test'声明接口'MyInterface'但没有实现它:类型'MyInterface'需要一个构造签名,但类型'test'缺少一个”在尝试编译它时。
EDIT:
编辑:
So after researching this a bit more given the feedback.
因此,在根据反馈对这一点进行更多研究之后。
interface MyInterface {
new () : MyInterface;
}
class test implements MyInterface {
constructor () => test { return this; }
}
Is not valid TypeScript and this does not solve the problem. You can not define the return type of the constructor. It will return "test". The signature of the following: class test { constructor () { } } Seems to be "new () => test" (obtained by hovering over "class" in the online editor with just that code pasted in). And this is what we would want and what i thought it would be.
不是有效的 TypeScript,这不能解决问题。您不能定义构造函数的返回类型。它将返回“测试”。以下签名: class test { constructor () { } } 似乎是“new () => test”(通过将鼠标悬停在在线编辑器中的“class”上并粘贴该代码获得)。这就是我们想要的,也是我认为的。
Can anyone provide an example of this or something similar where it is actually compiling?
任何人都可以在实际编译的地方提供这个或类似的例子吗?
EDIT (again...):
编辑(再次...):
So I might have come up with an idea as to why it is possible to define this in an interface but not possible to implement in a TypeScript class.The following works:
所以我可能想出了一个想法,为什么可以在接口中定义它,但不能在 TypeScript 类中实现。以下工作:
var MyClass = (function () {
function MyClass() { }
return MyClass;
})();
interface MyInterface {
new () : MyInterface;
}
var testFunction = (foo: MyInterface) : void => { }
var bar = new MyClass();
testFunction(bar);
So is this only a feature of TypeScript that lets you interface javascript? Or is it possible to implement it in TypeScript without having to implement the class using javascript?
那么这仅仅是 TypeScript 的一个特性,它可以让你接口 javascript 吗?或者是否可以在 TypeScript 中实现它而不必使用 javascript 实现类?
回答by Ryan Cavanaugh
Construct signatures in interfaces are not implementable in classes; they're only for defining existing JS APIs that define a 'new'-able function. Here's an example involving interfaces new
signatures that does work:
接口中的构造签名不能在类中实现;它们仅用于定义定义“新”功能的现有 JS API。这是一个涉及new
确实有效的接口签名的示例:
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);
This creates an actual constraint for what you can invoke makeObj
with:
这为您可以调用的内容创建了一个实际约束makeObj
:
class Other implements ComesFromString {
constructor (public name: string, count: number) {
}
}
makeObj(Other); // Error! Other's constructor doesn't match StringConstructable
回答by Nils
On my search for the exact same question I went looking how the TypeScript-Team did that...
在我搜索完全相同的问题时,我开始查看 TypeScript 团队是如何做到的……
They are declaring an interface and afterwards a variable with a name matching exactly the interface-name. This is also the way to type static functions.
他们声明了一个接口,然后是一个名称与接口名称完全匹配的变量。这也是键入静态函数的方式。
Example from lib.d.ts
:
示例来自lib.d.ts
:
interface Object {
toString(): string;
toLocaleString(): string;
// ... rest ...
}
declare var Object: {
new (value?: any): Object;
(): any;
(value: any): any;
// ... rest ...
}
I tried that and it works like charm.
我试过了,它就像魅力一样。
回答by egelev
Well an interface with a construct signature is not meant to be implemented by any class(at first glance this might look weird for guys with C#/Java background like me but give it a chance). It is slightly different.
好吧,具有构造签名的接口并不意味着由任何类实现(乍一看,这对于像我这样具有 C#/Java 背景的人来说可能看起来很奇怪,但给它一个机会)。它略有不同。
For a moment think of it as a interface with a call signature(like a @FunctionalInterface in Java world). Its purpose is to describe a function type..kind of. The described signature is supposed to be satisfied by a function object...but not just any high level function or a method. It should be a function which knows how to construct an object, a function that gets called when new
keyword is used.
暂时把它想象成一个带有调用签名的接口(就像 Java 世界中的 @FunctionalInterface)。它的目的是描述一个函数类型……某种。所描述的签名应该由函数对象满足……但不仅仅是任何高级函数或方法。它应该是一个知道如何构造对象的函数,一个在使用new
关键字时被调用的函数。
So an interface with a construct signature defines the signature of a constructor ! The constructor of your class that should comply with the signature defined in the interface(think of it as the constructor implements the interface). It is like a factory !
因此,具有构造签名的接口定义了构造函数的签名!您的类的构造函数应符合接口中定义的签名(将其视为构造函数实现了接口)。它就像一个工厂!
Here is a short snippet of code that tries to demonstrate the most common usage:
下面是一小段代码,试图演示最常见的用法:
interface ClassicInterface { // old school interface like in C#/Java
method1();
...
methodN();
}
interface Factory { //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 Factory
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 Factory
constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface
method1() {}
...
methodN() {}
}
// And here is the polymorphism of construction
function instantiateClassicInterface(ctor: Factory, 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");
回答by Fenton
From a design perspective, it isn't usual to specify the constructor requirements in an interface. The interface should describe the operations you can perform on an object. Different classes that implement the interface should be allowed to require different constructor parameters if they need to.
从设计的角度来看,在接口中指定构造函数要求并不常见。接口应该描述您可以对对象执行的操作。如果需要,应该允许实现接口的不同类需要不同的构造函数参数。
For example, if I had an interface:
例如,如果我有一个接口:
interface ISimplePersistence {
load(id: number) : string;
save(id: number, data: string): void;
}
I might have implementations for storing the data as a cookie, which needs no constructor parameters, and a version that stores the data in a database, which needs a connection string as a constructor parameter.
我可能有将数据存储为 cookie 的实现,它不需要构造函数参数,以及一个将数据存储在数据库中的版本,它需要一个连接字符串作为构造函数参数。
If you are still want to define constructors in an interface, there is a dirty way to do this, which I used to answer this question:
如果你仍然想在接口中定义构造函数,有一个肮脏的方法来做到这一点,我曾经回答过这个问题:
回答by MarvinDV
To achieve the intended behaviour you could use Decorators, even though that is probably not what they are supposed to be used for.
为了实现预期的行为,您可以使用Decorators,即使这可能不是它们应该用于的目的。
This
这个
interface MyInterface {
new ();
}
function MyInterfaceDecorator(constructor: MyInterface) {
}
@MyInterfaceDecorator
class TestClass {
constructor () { }
}
compiles without a problem. In contrast, the following definition for TestClass
编译没有问题。相比之下,TestClass 的以下定义
// error TS2345: Argument of type 'typeof TestClass' is not assignable to parameter of type 'MyInterface'.
@MyInterfaceDecorator
class TestClass {
constructor (arg: string) { }
}
will not compile.
不会编译。
回答by DomQ
To expand on Nils' answer, you can also make a generic new
-able function with the same technique:
要扩展Nils的答案,您还可以new
使用相同的技术创建一个通用的函数:
interface MyArrayConstructor {
<T>(...elements: Array<T>): MyArrayInstance<T>
new <T> (...elements: Array<T>): MyArrayInstance<T>
}
// “Typecast” not the function itself, but another symbol,
// so that the body of myArray will also benefit from
// type-checking:
export const MyArray = myArray as MyArrayConstructor
interface MyArrayInstance<T> {
push(...args: Array<T>): number
slice(from?: number, to?:number): Array<T>
}
function myArray(...elements: Array<T>): MyArrayInstance<T> {
return {
push(...args) { ... },
slice(from?: number, to?: number) { ... }
}
}
回答by Azat
From the official documentation
来自官方文档
This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
Instead, you would need to work with the static side of the class directly. In this example, we define two interfaces, ClockConstructor for the constructor and ClockInterface for the instance methods. Then, for convenience, we define a constructor function createClock that creates instances of the type that is passed to it:
这是因为当一个类实现一个接口时,只检查该类的实例端。由于构造函数位于静态端,因此不包括在此检查中。
相反,您需要直接使用类的静态端。在这个例子中,我们定义了两个接口,用于构造函数的 ClockConstructor 和用于实例方法的 ClockInterface。然后,为了方便起见,我们定义了一个构造函数 createClock 来创建传递给它的类型的实例:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
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);