サーチ…


前書き

interfacesは、そのインタフェースを実装しているどのクラスでも期待できるフィールドと関数のリストを指定します。逆に、インタフェースで指定されたすべてのフィールドと関数を持たない限り、クラスはインタフェースを実装できません。

インターフェイスを使用する主な利点は、多態的な方法で異なるタイプのオブジェクトを使用できることです。これは、インタフェースを実装しているどのクラスも少なくともそれらのフィールドと関数を持っているからです。

構文

  • interface InterfaceName {
  • parameterName:parameterType;
  • optionalParameterName?:parameterType;
  • }

備考

インタフェースとタイプエイリアス

インタフェースは、オブジェクトの形状を指定するのに適しています。たとえば、指定できるpersonオブジェクト

interface person {
    id?: number;
    name: string;
    age: number;
}

しかし、人がSQLデータベースに格納されている方法を表現したいとしたらどうでしょうか?各DBエントリがshape [string, string, number] (文字列または数値の配列)の行からなるので、行にプロパティがないため、これをオブジェクトシェイプとして表現する方法はありませんそれは単なる配列です。

これはタイプが有用になる機会です。行パラメータfunction processRow(row: [string, string, number])を受け入れるすべての関数で指定する代わりに、行ごとに別々の型エイリアスを作成し、それをすべての関数で使用することができます。

type Row = [string, string, number];
function processRow(row: Row)

公式のインターフェイスのドキュメント

https://www.typescriptlang.org/docs/handbook/interfaces.html

既存のインターフェイスに関数またはプロパティを追加する

JQuery型定義への参照があり、それを拡張して私たちがインクルードしたプラグインの追加機能を持つようにし、正式な型定義を持たないものとしましょう。プラグインによって追加された関数を、同じJQuery名を持つ別のインタフェース宣言で宣言することで、簡単に拡張できます。

interface JQuery {
  pluginFunctionThatDoesNothing(): void;

  // create chainable function
  manipulateDOM(HTMLElement): JQuery;
}

コンパイラは、同じ名前を持つすべての宣言を1つにマージします。詳細については、 宣言のマージを参照してください。

クラスインタフェース

インターフェース内でpublic変数とメソッドの型を宣言して、他の型コードがどのように相互作用できるかを定義します。

interface ISampleClassInterface {
  sampleVariable: string;

  sampleMethod(): void;
  
  optionalVariable?: string;
}

ここでは、インターフェイスを実装するクラスを作成します。

class SampleClass implements ISampleClassInterface {
  public sampleVariable: string;
  private answerToLifeTheUniverseAndEverything: number;

  constructor() {
    this.sampleVariable = 'string value';
    this.answerToLifeTheUniverseAndEverything = 42;
  }

  public sampleMethod(): void {
    // do nothing
  }
  private answer(q: any): number {
    return this.answerToLifeTheUniverseAndEverything;
  }
}

この例では、インターフェイスISampleClassInterfaceおよびインターフェイスをimplementsするクラスSampleClassを作成する方法を示します。

インターフェイスの拡張

インターフェイスがあるとします。

interface IPerson {
    name: string;
    age: number;

    breath(): void;
}

人物と同じプロパティを持つより具体的なインターフェイスを作成したい場合は、 extendsキーワードを使用してそれを実行できます。

interface IManager extends IPerson {
    managerId: number;

    managePeople(people: IPerson[]): void;
}

さらに、複数のインタフェースを拡張することも可能です。

インタフェースを使用した型の強制

Typescriptの主な利点の1つは、間違いを防ぐためにコードを渡すデータ型の値を強制することです。

あなたがペットデートのアプリケーションを作っているとしましょう。

あなたは2つのペットがお互いに互換性があるかどうかをチェックするこの簡単な機能を持っています...

checkCompatible(petOne, petTwo) {
  if (petOne.species === petTwo.species &&
      Math.abs(petOne.age - petTwo.age) <= 5) {
    return true;
  }
}

これは完全に機能的なコードですが、誰か、特にこの機能を書かなかったこのアプリケーションで作業している他の人が、「種」と「年齢」のオブジェクトを渡すことになっていることを知らないと、プロパティ。誤ってcheckCompatible(petOne.species, petTwo.species)を試してから、関数がpetOne.species.speciesまたはpetOne.species.ageにアクセスしようとしたときにスローされるエラーを把握することができます。

これを防ぐための1つの方法は、ペットパラメータに必要なプロパティを指定することです。

checkCompatible(petOne: {species: string, age: number}, petTwo: {species: string, age: number}) {
    //...
} 

この場合、Typescriptは、関数に渡されるすべてのものが 'species'と 'age'のプロパティを持っていることを確認しますが、追加のプロパティがあれば問題ありませんが、これは2つのプロパティを指定しても扱いにくいソリューションです。インターフェイスでは、より良い方法があります!

最初にインタフェースを定義します。

interface Pet {
  species: string;
  age: number;
  //We can add more properties if we choose.
}

今私たちがしなければならないのは、私たちの新しいインターフェイスとしてのパラメータのタイプを指定することです。

checkCompatible(petOne: Pet, petTwo: Pet) {
  //...
}

...そしてTypescriptは、関数に渡されたパラメータにPetインタフェースで指定されたプロパティが含まれていることを確認します!

汎用インタフェース

クラスと同様に、インタフェースは多態的なパラメータ(ジェネリックともいう)も受け取ることができます。

インタフェースの汎用パラメータの宣言

interface IStatus<U> {
    code: U;
}

interface IEvents<T> {
    list: T[];
    emit(event: T): void;
    getAll(): T[];
}

ここでは、2つのインタフェースにいくつかの一般的なパラメータTUがあることがわかります。

汎用インタフェースの実装

インタフェースIEventを実装するための簡単なクラスを作成します。

class State<T> implements IEvents<T> {
    
    list: T[];
    
    constructor() {
        this.list = [];
    }
    
    emit(event: T): void {
        this.list.push(event);
    }
    
    getAll(): T[] {
        return this.list;
    }
    
}

Stateクラスのいくつかのインスタンスを作成しましょう。

この例では、 StateクラスはIStatus<T>を使用して汎用ステータスを処理します。このように、インターフェースIEvent<T>も処理しますIStatus<T>

const s = new State<IStatus<number>>();

// The 'code' property is expected to be a number, so:
s.emit({ code: 200 }); // works
s.emit({ code: '500' }); // type error 

s.getAll().forEach(event => console.log(event.code));

ここで、 StateクラスはISatus<number>とタイプされます。


const s2 = new State<IStatus<Code>>();

//We are able to emit code as the type Code
s2.emit({ code: { message: 'OK', status: 200 } });

s2.getAll().map(event => event.code).forEach(event => {
    console.log(event.message);
    console.log(event.status);
});

私たちのStateクラスはIStatus<Code>とタイプされます。このようにして、より複雑な型をemitメソッドに渡すことができます。

ご覧のように、汎用インタフェースは静的型付きコードにとって非常に便利なツールです。

多態性のためのインタフェースの使用

インタフェースを使用して多態性を実現し、将来的にインターフェイスのメソッドを実装することで開発者が独自の方法で実装できるようにする主な理由。

インターフェイスと3つのクラスがあるとします。

interface Connector{
    doConnect(): boolean;
}

これはコネクタインターフェイスです。これでWi-Fi通信用に実装します。

export class WifiConnector implements Connector{

    public doConnect(): boolean{
        console.log("Connecting via wifi");
        console.log("Get password");
        console.log("Lease an IP for 24 hours");
        console.log("Connected");
        return true
    }

}

ここでは独自の実装を持つWifiConnectorという具体的なクラスを開発しました。今はこれがConnectorです。

今度は、コンポーネントConnectorを持つSystemを作成しています。これは依存性注入と呼ばれます。

export class System {
    constructor(private connector: Connector){ #inject Connector type
        connector.doConnect()
    }
}

constructor(private connector: Connector)この行は非常に重要です。 Connectorはインターフェイスであり、 doConnect()必要です。 Connectorはインターフェイスなので、このクラスのSystemははるかに柔軟性があります。 Connectorインターフェースを実装したTypeはすべて渡すことができます。将来の開発者はより柔軟に対応できます。たとえば、開発者はBluetooth接続モジュールを追加したいと考えています。

export class BluetoothConnector implements Connector{

    public doConnect(): boolean{
        console.log("Connecting via Bluetooth");
        console.log("Pair with PIN");
        console.log("Connected");
        return true
    }

}

Wi-FiとBluetoothには独自の実装があることを確認してください。接続する方法はそれぞれ異なります。しかし、どちらもType Connectorを実装しています。これはType Connectorです。そのため、それらのいずれかをコンストラクタパラメータとしてSystemクラスに渡すことができます。これを多型といいます。クラスSystem今では、Bluetooth /無線LANでも、我々はInferade、Bluetooth5のような他の通信モジュールを追加することができますし、全くだけ実装することによってであるかどうかを認識していないConnectorインターフェースを。

これはダックタイピングと呼ばれていますdoConnect()は単なるプレースホルダであり、開発者はこれを自分のものとして実装しているため、 Connectorタイプは動的です。

constructor(private connector: WifiConnector)WifiConnectorが具体的なクラスの場合はどうなりますか?それから、 SystemクラスはWifiConnectorと密接にしか結びつきません。ここでインターフェイスは多型によって私たちの問題を解決しました。

暗黙の実装とオブジェクトシェイプ

TypeScriptはインターフェイスをサポートしていますが、コンパイラはJavaScriptを出力しません。したがって、インタフェースはコンパイル・ステップで事実上失われます。このため、インタフェースの型チェックは、オブジェクトの形状 (つまり、オブジェクトがインタフェース上のフィールドと関数をサポートしているかどうか)に依存し、インタフェースが実際に実装されているかどうかに依存しません。

interface IKickable {
  kick(distance: number): void;
}
class Ball {
  kick(distance: number): void {
    console.log("Kicked", distance, "meters!");
  }
}
let kickable: IKickable = new Ball();
kickable.kick(40);

Ballが明示的にIKickable実装していなくても、タイプが指定されていても、 IKickableBallインスタンスを割り当てることができます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow