TypeScript では、インターフェイスは制約として使用されます。JavaScript にはインターフェイスの概念がないため、JavaScript にコンパイルされると、すべてのインターフェイスが消去されます。
簡単な例を見てみましょう:
function printLabel(labelledObj: { label: string }) { console.log(labelledObj.label); } var myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj);
このメソッドでは、labeledObj の型は {label: string} となっており、少し複雑に見えるかもしれませんが、以下の myObj の宣言を見ると、これが size 属性 (値は 10)、ラベル属性を持つオブジェクト (値は「サイズ 10 オブジェクト」)。したがって、メソッド パラメータ labeledObj の型は {label: string} です。これは、パラメータが string 型の label 属性を持つことを意味します。
しかし、こうして書くと、この方法はまだ少しわかりにくいように思えます。次に、インターフェイスを使用して、このメソッドのパラメータ タイプを定義できます。
interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } var myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj);
オプションの属性
場合によっては、属性が存在する必要がないため、オプションの属性を使用して定義できます。
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): { color: string; area: number } { var newSquare = { color: "white", area: 100 }; if (config.color) { newSquare.color = config.color; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } var mySquare = createSquare({ color: "black" });
次に、SquareConfig インターフェイスを実装するオブジェクトを createSquare メソッドに渡します。
それは完全に必要不可欠なものであるのに、なぜそれを定義する必要があるのでしょうか?オプションのプロパティを定義すると、まったく定義しない場合と比べて 2 つの利点があります。 1. 属性がある場合、型が制限される可能性があります。これは非常に重要です。2. メソッド本体に誤って color を color として記述すると、コンパイルが失敗します。
メソッドタイプ
JavaScript ではメソッド関数が基本型です。オブジェクト指向の考え方では、インターフェイスの実装はクラスによって実現されますが、関数はインターフェイスを実装できますか?答えは「はい」です。
TypeScript では、インターフェイスを使用してメソッドのシグネチャを制約できます。
interface SearchFunc { (source: string, subString: string): boolean; } var mySearch: SearchFunc; mySearch = function(source: string, subString: string) { var result = source.search(subString); if (result == -1) { return false; } else { return true; } }
上記のコードでは、メソッドのシグネチャを制約するインターフェイスを定義しています。このメソッドには 2 つの文字列パラメータがあり、ブール値を返します。コードの 2 番目の部分では、このインターフェイスの実装を宣言します。
コンパイラーは型 (パラメーターの型、戻り値の型) が正しいかどうかのみをチェックするため、パラメーター名を別の名前に変更できることに注意してください。
var mySearch: SearchFunc; mySearch = function(src: string, sub: string) { var result = src.search(sub); if (result == -1) { return false; } else { return true; } }
これはコンパイルして渡すこともできます。
配列型
上記ではインターフェイスでメソッドの型を定義しましたが、配列の型はどのように定義すればよいでしょうか?とてもシンプルです。
interface StringArray { [index: number]: string; } var myArray: StringArray; myArray = ["Bob", "Fred"];
myArray は配列であり、インデクサーの型は数値であり、要素は文字列です。
インターフェイスの定義では、インデクサーの名前は通常、index です (もちろん、他の名前に変更することもできますが、通常は名前がインデックスのままになります)。したがって、それを
に変更しますinterface StringArray { [myIndex: number]: string; } var myArray: StringArray; myArray = ["Bob", "Fred"];
それもOKです。
インデクサーのタイプは数値または文字列のみであることに注意してください。
interface Array{ [index: number]: any; } interface Dictionary{ [index: string]: any; }
上記の 2 つの段落はコンパイルして渡すことができます。
最後に注意すべきことは、インターフェイスがすでに配列型である場合、インターフェイスで定義されている他の属性の型は配列の要素型である必要があるということです。例:
interface Dictionary { [index: string]: string; length: number; // error, the type of 'length' is not a subtype of the indexer }
この場合、コンパイルされず、長さを文字列型に変更する必要があります。
クラスを使用してインターフェースを実装する
通常の状況では、上記のようにインターフェイスを直接使用するのではなく、クラスを使用して必要なインターフェイスを実装することに慣れています。
interface ClockInterface { currentTime: Date; } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
TypeScript では、class キーワードを使用して宣言します。これは EcmaScript 6 と同じです。
さらに、インターフェイスを使用して、クラスで定義されたメソッドを制約することができます。
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
TypeScript では、インターフェイスのコンストラクターを定義できます。
interface ClockInterface { new (hour: number, minute: number); }
もし私たちが素朴であれば、次のように書くかもしれません:
interface ClockInterface { new (hour: number, minute: number); } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
これではうまくいきません! ! !コンストラクターは静的であるため、クラスはインターフェイスのインスタンス部分のみを実装できます。
では、このインターフェースで定義されたコンストラクターは効果がないのでしょうか? TypeScript はこの機能を提供するため、決して役に立たないわけではありません。宣言されたメソッドはかなり特殊です:
interface ClockStatic { new (hour: number, minute: number); } class Clock { currentTime: Date; constructor(h: number, m: number) { } } var cs: ClockStatic = Clock; var newClock = new cs(7, 30);
通常は新しい Clock を作成しますが、ここでは Clock クラスを ClockStatic インターフェイスに指定します。 newClock変数の型は任意であることに注意してください。
継承されたインターフェース
クラスと同様に、インターフェイスでも extends キーワードを使用して継承を実装できます。
interface Shape { color: string; } interface Square extends Shape { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10;
もちろん、複数のインターフェイスを継承することもできます。
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
複数のインターフェースの継承はサポートされていますが、継承したインターフェースに定義されている同名のプロパティの型が異なる場合はコンパイルが通らないことに注意してください。
interface Shape { color: string; test: number; } interface PenStroke { penWidth: number; test: string; } interface Square extends Shape, PenStroke { sideLength: number; }
那么这段代码就无法编译通过了,因为 test 属性的类型无法确定。
同时使用上面所述的类型
如果仅能单一使用某种类型,那么这接口也未免太弱了。但幸运的是,我们的接口很强大。
interface Counter { (start: number): string; interval: number; reset(): void; } var c: Counter; c(10); c.reset(); c.interval = 5.0;
这样就使用到三种类型了,分别是方法(接口自己是个方法)、属性、方法(定义了方法成员)。
以上所述就是本文的全部内容了,希望大家能够喜欢。