インターフェースは、アプリケーションにおける契約を定義する構造体です。 これはクラスが従うべき構文を定義する。
TypeScriptコンパイラーはインターフェイスをJavaScriptに変換することはありません。 それは型チェックのためにインターフェイスを使用します。 これは「ダックタイピング」または「構造的サブタイピング」としても知られています。
インターフェースはキーワード interface
で定義され、関数または矢印関数を使用してプロパティとメソッドの宣言を含めることができます。
Copy
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
上記の例では、IEmployee
インターフェースは 2 つのプロパティ empCode
と empName
を含んでいます。 また、1つの数値パラメタと数値戻り値型を含む矢印関数を使ったメソッド宣言getSalaray
も含まれている。 getManagerName
メソッドは通常の関数を用いて宣言されている。 これは、IEmployee
型のオブジェクトは、2つのプロパティと2つのメソッドを定義しなければならないことを意味する。
型としてのインターフェース
TypeScriptのインターフェースは、型を定義し、それをクラスで実装するために使用することができます。 型としてのインタフェース コピー
interface KeyPair { key: number; value: string;}let kv1: KeyPair = { key:1, value:"Steve" }; // OKlet kv2: KeyPair = { key:1, val:"Steve" }; // Compiler Error: 'val' doesn't exist in type 'KeyPair'let kv3: KeyPair = { key:1, value:100 }; // Compiler Error:
上記の例では、インタフェースKeyPair
は2つのプロパティkey
とvalue
を含んでいます。 変数 kv1
は KeyPair
型として宣言されている。 従って、KeyPair
と同じ構造に従わなければならない。 つまり、変数kv1
に代入できるのは、数値型のプロパティkey
と文字列型のプロパティvalue
を持つオブジェクトだけである。 プロパティの名前に変更があったり、データ型がKeyPair
と異なったりすると、TypeScriptコンパイラはエラーを表示する。 別の変数kv2
もKeyPair
型として宣言されているが、代入される値はvalue
ではなくval
なのでエラーとなる。 同じようにkv3ではvalue
のプロパティに数値が代入されているので、コンパイラはエラーを表示する。 このように、TypeScriptではオブジェクトの構造を適切に保つために、インターフェイスを利用している。
関数の型としてのインターフェース
TypeScriptのインターフェースは、関数の型を定義するためにも使われる。 これにより、関数のシグネチャが確保されます。
Copy
interface KeyValueProcessor{ (key: number, value: string): void;};function addKeyValue(key:number, value:string):void { console.log('addKeyValue: key = ' + key + ', value = ' + value)}function updateKeyValue(key: number, value:string):void { console.log('updateKeyValue: key = '+ key + ', value = ' + value)} let kvp: KeyValueProcessor = addKeyValue;kvp(1, 'Bill'); //Output: addKeyValue: key = 1, value = Bill kvp = updateKeyValue;kvp(2, 'Steve'); //Output: updateKeyValue: key = 2, value = Steve
上記の例では、インターフェースKeyValueProcessor
にメソッドシグネチャが含まれています。 これは、関数の型を定義するものです。 ここで、KeyValueProcessor
型の変数を定義すると、KeyValueProcessor
インターフェースで定義されているのと同じシグネチャを持つ関数だけを指すことができるようになります。 つまり、addKeyValue
やupdateKeyValue
の関数がkvp
に代入されるわけです。 つまり、kvp
は関数のように呼び出すことができる。
異なるシグネチャを持つ関数を割り当てようとすると、エラーが発生します。
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
配列型のインターフェース
インターフェースは、値と同様にインデックスの型を定義することができる配列の型も定義することが可能です。 配列の型 コピー
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
上記の例では、インタフェース NumList
は、インデックスを数値、値を数値型とする配列の型を定義しています。 同様に、IStringList
はindexを文字列、valueを文字列とする文字列配列を定義している。
オプションのプロパティ
時には、過剰なプロパティを持つインターフェースを宣言しても、全てのオブジェクトが与えられたインターフェースのプロパティを全て定義することを期待しないかもしれません。 我々は、”? “でマークされたオプションのプロパティを持つことができます。 このような場合、インターフェースのオブジェクトはこれらのプロパティを定義してもしなくてもかまいません。
Copy
interface IEmployee { empCode: number; empName: string; empDept?:string;}let empObj1:IEmployee = { // OK empCode:1, empName:"Steve"}let empObj2:IEmployee = { // OK empCode:1, empName:"Bill", empDept:"IT"}
上記の例では、empDept
は ?
でマークされているので、IEmployee
のオブジェクトはこのプロパティを含んでいてもいなくてもよい。
読み取り専用プロパティ
TypeScript ではプロパティを読み取り専用としてマークできる方法がある。 これは、プロパティに値が割り当てられると、変更できなくなることを意味します!
Copy
interface Citizen { name: string; readonly SSN: number;}let personObj: Citizen = { SSN: 110555444, name: 'James Bond' }personObj.name = 'Steve Smith'; // OKpersonObj.SSN = '333666888'; // Compiler Error
上記の例では、SSN
プロパティが読み取り専用になっています。 Citizen型のpersonObjオブジェクトを定義し、2つのインターフェースプロパティに値を代入しています。 次に、name
とSSN
の2つのプロパティに割り当てられた値を変更しようとする。 読み取り専用のSSN
プロパティを変更しようとすると、TypeScriptコンパイラーはエラーを表示する。
インターフェイスの拡張
インターフェイスは1つ以上のインターフェイスを拡張することができる。 これにより、インターフェースを柔軟に記述し、再利用できるようになります。
Copy
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
上の例では、IEmployee
インターフェースが IPerson
インターフェースを拡張しています。
インターフェイスの実装
JavaやC#などの言語と同様に、TypeScriptのインターフェイスはクラスで実装することができる。 インターフェイスを実装したクラスは、インターフェイスの構造に厳密に準拠する必要があります。 インターフェースの実装 コピー
interface IEmployee { empCode: number; name: string; getSalary:(number)=>number;}class Employee implements IEmployee { empCode: number; name: string; constructor(code: number, name: string) { this.empCode = code; this.name = name; } getSalary(empCode:number):number { return 20000; }}let emp = new Employee(1, "Steve");
上記の例では、 Employee クラスに implement キーワードを使用して IEmployee
インターフェースが実装されます。 実装するクラスは、プロパティと関数を同じ名前とデータ型で厳密に定義する必要があります。 実装クラスがこの構造に従っていない場合、コンパイラはエラーを表示します。
もちろん、実装クラスは追加のプロパティやメソッドを定義することができますが、少なくともインターフェースのすべてのメンバーを定義する必要があります。