我在解决如何在接口中定义构造函数工作方面遇到了一些麻烦。我可能完全误解了一些事情。但我已经寻找了很长一段时间的答案,我找不到任何与此相关的东西。

我如何在TypeScript类中实现以下接口:

interface MyInterface {
    new ( ... ) : MyInterface;
}

Anders Hejlsberg创建了一个包含类似于这个视频(大约14分钟)的界面。但是无论如何我都不能在一个类中实现这个。

我可能误解了什么,我没明白什么?

编辑:

澄清。用“new(…)”我的意思是“任何事”。我的问题是,我甚至不能得到这个工作的最基本的版本:

interface MyInterface {
    new () : MyInterface;
}

class test implements MyInterface {
    constructor () { }
}

这不是编译对我来说,我得到“类'test'声明接口'MyInterface',但没有实现它:类型'MyInterface'需要一个构造签名,但类型'test'缺乏一个”,当试图编译它。

编辑:

所以在研究了更多的反馈之后。

interface MyInterface {
    new () : MyInterface;
}

class test implements MyInterface {
    constructor () => test { return this; }
}

不是有效的TypeScript,这并不能解决问题。不能定义构造函数的返回类型。它将返回"test"。以下签名: 类测试{ 构造函数(){} } 似乎是“new () => test”(通过将鼠标悬停在在线编辑器中的“class”上并粘贴该代码获得)。这就是我们想要的,也是我想要的。

谁能提供一个这样的例子或类似的东西,它实际上是编译的?

编辑(…):

所以我可能想到了一个想法,为什么可以在接口中定义这个,但不可能在TypeScript类中实现。以下工作:

var MyClass = (function () {
    function MyClass() { }
    return MyClass;
})();

interface MyInterface {
    new () : MyInterface;
}

var testFunction = (foo: MyInterface) : void =>  { }
var bar = new MyClass();
testFunction(bar);

那么,这仅仅是TypeScript的一个功能,让你可以接口javascript吗?或者有可能在TypeScript中实现它,而不必使用javascript实现类?


当前回答

从设计的角度来看,在接口中指定构造函数需求并不常见。接口应该描述您可以在对象上执行的操作。应该允许实现接口的不同类在需要时要求不同的构造函数参数。

例如,如果我有一个接口:

interface ISimplePersistence {
    load(id: number) : string;
    save(id: number, data: string): void;
}

我可能有将数据存储为cookie的实现(不需要构造函数参数),以及将数据存储在数据库中的版本(需要连接字符串作为构造函数参数)。

如果你仍然想在接口中定义构造函数,有一种很脏的方法可以做到这一点,我用它来回答这个问题:

带有构造签名而不是类型检查的接口

其他回答

接口中的构造签名不能在类中实现;它们只用于定义现有的定义“新”功能的JS api。下面是一个涉及接口新签名的例子:

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

这为你可以调用makeObj创建了一个实际的约束:

class Other implements ComesFromString {
    constructor (public name: string, count: number) {
    }
}

makeObj(Other); // Error! Other's constructor doesn't match StringConstructable

从设计的角度来看,在接口中指定构造函数需求并不常见。接口应该描述您可以在对象上执行的操作。应该允许实现接口的不同类在需要时要求不同的构造函数参数。

例如,如果我有一个接口:

interface ISimplePersistence {
    load(id: number) : string;
    save(id: number, data: string): void;
}

我可能有将数据存储为cookie的实现(不需要构造函数参数),以及将数据存储在数据库中的版本(需要连接字符串作为构造函数参数)。

如果你仍然想在接口中定义构造函数,有一种很脏的方法可以做到这一点,我用它来回答这个问题:

带有构造签名而不是类型检查的接口

在我搜索完全相同的问题时,我去看TypeScript-Team是如何做到的…

它们首先声明一个接口,然后声明一个与接口名称完全匹配的变量。这也是输入静态函数的方法。

来自lib.d.ts的例子:

interface Object {
    toString(): string;
    toLocaleString(): string;
    // ... rest ...
}
declare var Object: {
    new (value?: any): Object;
    (): any;
    (value: any): any;
    // ... rest ...
}

我试过了,效果很不错。

为了实现预期的行为,您可以使用decorator,即使这可能不是它们应该被用于的目的。

This

interface MyInterface {
    new ();
}

function MyInterfaceDecorator(constructor: MyInterface) {
}


@MyInterfaceDecorator
class TestClass {
    constructor () { }
}

编译没有问题。相反,下面是TestClass的定义

// error TS2345: Argument of type 'typeof TestClass' is not assignable to parameter of type 'MyInterface'.
@MyInterfaceDecorator
class TestClass {
    constructor (arg: string) { }
}

不会编译。

为了扩展Nils的答案,你也可以用相同的技术创建一个通用的new-able函数:

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) { ... }
  }
}