web-dev-qa-db-ja.com

TypeScriptのインターフェイスとクラス

C#には、インターフェイスとクラスの間に非常に大きな違いがあります。実際、クラスは参照型を表すため、そのクラスでモデル化されたオブジェクトを実際に作成できますが、インターフェイスは、特定の動作の存在を保証するためにクラスが署名するコントラクトを意味します。特に、インターフェイスのインスタンスを作成することはできません。

インターフェイスの重要なポイントは、動作を公開することです。クラスは、上記の動作を明示的に1つ実装することで実装します。

その場合、インターフェイスmayにはプロパティが含まれていますが、ほとんどの場合、動作の問題のためにインターフェイスが重要です。したがって、ほとんどのタイプのインターフェイスは、単なる動作の契約です。

一方、TypeScriptの場合、私は非常に不安にさせたように見えます。実際、これを複数回目にしました。これがこの質問の理由です。

あるチュートリアルで私はこれを見ました:

export interface User {
    name: string; // required with minimum 5 chracters
    address?: {
        street?: string; // required
        postcode?: string;
    }
}

しかし、ちょっと待ってください。なぜUserがインターフェースなのですか? C#のように考える場合、Userをインターフェイスにしないでください。実際、それを見ると、動作のコントラクトではなく、データ型Userを定義しているようです。

C#で行うように考えると、自然なことは次のようになります。

export class User {
    public name: string;
    public address: Address;
}
export class Address {
    public street: string;
    public postcode: string;
}

しかし、動作のコントラクトを定義するのではなく、単にデータ型を定義するために、クラスで行うようにインターフェイスを使用するこのことは、TypeScriptでは非常に一般的のようです。

それでは、TypeScriptではどのようなインターフェイスが意図されていますか? C#でクラスを使用するように、TypeScriptでインターフェイスを使用するのはなぜですか? TypeScriptでインターフェイスを適切に使用する方法:動作のコントラクトを確立するため、またはプロパティとオブジェクトが持つべきものを定義するために?

20
user1620696

JavaScriptでは、データがプレーンオブジェクトとして、多くの場合JSONを介して交換されることを考慮してください。

let data = JSON.parse(someString);

このdataUserオブジェクトの配列であり、関数に渡すとしましょう:

data.forEach(user => foo(user))

fooは次のように入力されます。

function foo(user: User) { ... }

しかし、ちょっと待ってください、_new User!我々がすべき?クラスUsermapにすべてのdataを記述する必要がありますが、結果はまったく同じですが、プロパティを持つObject ?いいえ、それは型システムを満足させるためだけのものであり、ランタイムについては何も変更しません。ここでは、特定のオブジェクトがどのように(「振る舞う」ように)見えるかを説明する単純なinterfaceで十分です。

25
deceze

また、C#のバックグラウンドからTypeScriptにアクセスし、同じことを考えました。私はPOCOに沿って考えていました(POTOは問題ですか?)

それでは、TypeScriptではどのようなインターフェイスが意図されていますか?

TypeScriptハンドブック は、インターフェイスは「コード内のコントラクトを定義する」ことを意図しているようです。

C#でクラスを使用するように、TypeScriptでインターフェイスを使用するのはなぜですか?

ここで@decezeの答えに同意します。

ジョン・パパは、彼の ブログ でクラスとインターフェースのテーマを拡張しています。彼は、クラスが「継承と[そして]シングルトンオブジェクトを使用して、複数の新しいインスタンスを作成する」のに最適であることを示唆しています。そのため、TypeScriptハンドブックに記載されているTypeScriptインターフェイスの意図とある人の意見に基づいて、TypeScriptでコントラクトを確立するのにクラスは必要ないように思われます。代わりに、インターフェイスを使用する必要があります。 (あなたのC#の感覚はまだ気分を害するでしょう。)

インターフェイスはTypeScriptで適切に使用する必要があります。動作のコントラクトを確立するため、またはプロパティとオブジェクトを定義するために必要ですか?

私が質問を理解したら、インターフェイスはbehaviorまたはstructureの契約。これに対して、私は答えます:両方。 TypeScriptインターフェースは、C#またはJava(つまり、クラスの動作を記述するため)でインターフェースが使用されるのと同じ方法で使用できますが、データの構造を記述する機能も提供します。

さらに、私のインターフェイスはコンパイラーでコードを生成しないため、インターフェイスの代わりにクラスを使用するために同僚に依頼されました。

例:

このTypeScript:

class Car implements ICar {
    foo: string;
    bar(): void {
    }
}

interface ICar {
    foo: string;
    bar(): void;
}

このJavascriptを生成します:

var Car = (function () {
    function Car() {
    }
    Car.prototype.bar = function () {
    };
    return Car;
}());

試してみてください

12
Joe Hawkins

TypeScriptのインターフェイスは、両方ともコントラクトを提供するという点でC#のインターフェイスに似ています。ただし、メソッドのみを含むC#インターフェイスとは異なり、TypeScriptインターフェイスはオブジェクトに含まれるフィールドまたはプロパティを記述することもできます。したがって、C#インターフェイスでは直接不可能なことにも使用できます。

TypeScriptのインターフェイスとクラスの主な違いは、インターフェイスにはランタイム表現がなく、それらのコードが出力されないことです。インターフェイスは非常に幅広く使用できます。たとえば、オブジェクトリテラルを使用して、インターフェイスを満たすオブジェクトを構築できます。お気に入り:

let user: User = {
  name: 'abc',
  address: {
    street: 'xyz',
  },
};

または、任意のデータオブジェクト(たとえば、JSON解析を通じて受信)をインターフェイスに割り当てることができます(ただし、事前チェックでは、それが本当に有効なデータであると断定する必要があります)。したがって、インターフェイスはデータに対して非常に柔軟です。

一方、クラスには実行時に関連付けられた型があり、コードが生成されます。実行時にinstanceofを使用して型を確認できます。プロトタイプチェーンが設定されています。 Userをクラスとして定義すると、コンストラクター関数を呼び出さない限り、有効なユーザーにはなりません。そして、どんな種類の適切なデータもUserに定義することはできません。新しいインスタンスを作成し、プロパティをコピーする必要があります。

私の経験則:

  • (さまざまなソースの)純粋なデータを扱う場合、インターフェイスを使用します
  • アイデンティティーと状態(および状態を変更するためのメソッドがおそらく添付されている)をモデル化する場合、クラスを使用しています。
10
Matthias247

TypeScriptでインターフェイスを適切に使用する方法:動作のコントラクトを確立するため、またはプロパティとオブジェクトが持つべきものを定義するために?

TypeScriptのインターフェイスは形状のコントラクトであり、オブジェクトの予想される構造を記述します。値に特定のインターフェースアノテーションがある場合、その値は、インターフェースで定義されたメンバーを備えたオブジェクトであると予想されます。メンバーは、値または関数(メソッド)にすることができます。一般に、それらの動作(関数本体)は契約の一部ではありません。ただし、readonlyであるかどうかを指定できます。

それでは、TypeScriptではどのようなインターフェイスが意図されていますか? C#でクラスを使用するように、TypeScriptでインターフェイスを使用するのはなぜですか?

TypeScriptインターフェイスは、TypeScriptクラスによって実装されることが期待される場合、C#インターフェイスと同じ役割を果たすことができます。

しかし、クラスだけがインターフェースを実装できるわけではありません。どんな種類の値でも可能です:

interface HelloPrinter {
    printHello(): void
}

次のオブジェクトはクラスではありませんが、それでもインターフェースを実装しています。

{
    printHello: () => console.log("hello")
}

したがって、私たちはできる

const o: HelloPrinter = {
    printHello: () => console.log("hello")
}

typeScriptコンパイラは文句を言いません。

オブジェクトは、クラスを記述することを強制せずにインターフェイスを実装します。

インターフェースの操作は(インターフェースおよび)クラスの操作よりも軽量です。

ただし、実行時に型名(クラス/インターフェイス名)を知る必要がある場合、インターフェイス名はコンパイル時にしかわからないため、クラスが正しい選択です。

2
ideaboxer

ネイティブの逆シリアル化メカニズムのみを使用して、特定のクラスのインスタンスを逆シリアル化することはできません。デシリアライズできるのは、plain-old-javascript-objectのみです。このようなオブジェクトはTypeScriptインターフェイスに準拠できますが、クラスのインスタンスにすることはできません。 Webサービスから予想されるデータなど、シリアル化の境界を越えるデータを処理する必要がある場合は、インターフェイスを使用します。そのような値の新しいインスタンスを自分で生成する必要がある場合は、文字どおりに構築するか、それらを返す便利な関数(そのインターフェイスに準拠するオブジェクト)を作成します。

クラス自体がインターフェイスを実装できますが、ローカルに構築されたクラスインスタンスと、デシリアライズされた再構成されたプレーンオブジェクトの両方を処理する場合、混乱する可能性があります。オブジェクトのクラスベースに依存することは決してできないので、オブジェクトをその正確な目的のためのクラスとして定義するメリットもありません。

Webサービスからコードをやり取りするServerProxyモジュールの作成に成功しました-Webサービス呼び出しと返された結果。ノックアウトモデルなどにバインドしている場合、Webサービスのインターフェイスのみのコントラクトに準拠する返されたplain-old-javascript-objectをリフトする方法を知っているコンストラクターで、UIバインドモデルをカプセル化するクラスを作成できます。モデルクラスのインスタンス。

1
Jason Kleban