192

インターフェイスでのコンストラクターの定義がどのように機能するかを理解するのに苦労しています。私は何かを完全に誤解しているかもしれません。しかし、私はしばらくの間答えを探してきましたが、これに関連するものは何も見つかりません.

TypeScript クラスで次のインターフェイスを実装するにはどうすればよいですか。

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

Anders Hejlsberg は、このビデオ(約 14 分) で、これに似たものを含むインターフェイスを作成します。しかし、私の人生では、これをクラスに実装することはできません。

私はおそらく何かを誤解しています。何が得られていないのでしょうか?

編集:

明確にするために。「新しい ( ... )」とは、「何でも」という意味です。私の問題は、この作業の最も基本的なバージョンでさえ取得できないことです:

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 ではなく、これで問題は解決しません。コンストラクターの戻り値の型を定義することはできません。「テスト」を返します。次のシグネチャ: class test { constructor () { } } "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);

これは、javascript とのインターフェイスを可能にする TypeScript の唯一の機能ですか? または、javascriptを使用してクラスを実装することなく、TypeScriptで実装することは可能ですか?

4

7 に答える 7

189

インターフェイスのコンストラクト シグネチャは、クラスでは実装できません。これらは、「新しい」機能を定義する既存の 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);

これにより、呼び出すことができるものに対する実際の制約が作成されますmakeObj

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

makeObj(Other); // Error! Other's constructor doesn't match StringConstructable
于 2012-11-15T23:26:41.107 に答える
68

まったく同じ質問を探して、TypeScriptチームがそれをどのように行ったかを調べました...

それらはインターフェイスを宣言し、その後、インターフェイス名と正確に一致する名前を持つ変数を宣言しています。これは、静的関数を入力する方法でもあります。

からの例lib.d.ts:

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

私はそれを試してみましたが、それは魅力のように機能します。

于 2012-12-04T10:35:41.650 に答える
3

設計の観点からは、インターフェイスでコンストラクターの要件を指定することは通常ありません。インターフェイスは、オブジェクトに対して実行できる操作を記述する必要があります。必要に応じて、インターフェイスを実装するさまざまなクラスが、さまざまなコンストラクター パラメーターを要求できるようにする必要があります。

たとえば、インターフェイスがある場合:

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

コンストラクターのパラメーターを必要としない Cookie としてデータを格納するための実装と、コンストラクターのパラメーターとして接続文字列を必要とするデータベースにデータを格納するバージョンがあるかもしれません。

それでもインターフェイスでコンストラクターを定義したい場合は、これを行うための汚い方法があります。これを使用して、この質問に答えました。

型チェックを行わないコンストラクト シグネチャを持つインターフェイス

于 2012-11-15T23:15:31.957 に答える
1

意図した動作を実現するために、 Decoratorsを使用できますが、おそらくそれが使用されることになっているわけではありません。

これ

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

コンパイルされません。

于 2016-07-21T21:09:00.230 に答える
1

Nils ' answerを拡張するには、同じ手法でジェネリックな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) { ... }
  }
}
于 2020-05-21T08:20:03.483 に答える