Interface ist eine Struktur, die den Vertrag in Ihrer Anwendung definiert. Sie legt die Syntax fest, der die Klassen folgen müssen. Klassen, die von einer Schnittstelle abgeleitet sind, müssen der von ihrer Schnittstelle vorgegebenen Struktur folgen.
Der TypeScript-Compiler konvertiert Schnittstellen nicht in JavaScript. Er verwendet die Schnittstelle zur Typüberprüfung. Dies wird auch als „duck typing“ oder „structural subtyping“ bezeichnet.
Eine Schnittstelle wird mit dem Schlüsselwort interface
definiert und kann Eigenschaften und Methodendeklarationen mit einer Funktion oder einer Pfeilfunktion enthalten.
Kopie
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
Im obigen Beispiel enthält die Schnittstelle IEmployee
zwei Eigenschaften empCode
und empName
. Sie enthält auch eine Methodendeklaration getSalaray
mit einer Pfeilfunktion, die einen Zahlenparameter und einen Zahlenrückgabetyp enthält. Die Methode getManagerName
wird mit einer normalen Funktion deklariert. Das bedeutet, dass jedes Objekt vom Typ IEmployee
die zwei Eigenschaften und zwei Methoden definieren muss.
Schnittstelle als Typ
Schnittstelle in TypeScript kann verwendet werden, um einen Typ zu definieren und auch um ihn in der Klasse zu implementieren.
Die folgende Schnittstelle IEmployee
definiert einen Typ einer Variablen.
Kopieren
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:
Im obigen Beispiel enthält eine Schnittstelle KeyPair
zwei Eigenschaften key
und value
. Eine Variable kv1
wird als Typ KeyPair
deklariert. Sie muss also der gleichen Struktur wie KeyPair
folgen. Das bedeutet, dass nur ein Objekt mit den Eigenschaften key
vom Typ Zahl und value
vom Typ String einer Variablen kv1
zugewiesen werden kann. Der TypeScript-Compiler zeigt einen Fehler an, wenn sich der Name der Eigenschaften ändert oder der Datentyp anders ist als KeyPair
. Eine andere Variable kv2
ist ebenfalls als Typ KeyPair
deklariert, aber der zugewiesene Wert ist val
anstelle von value
, so dass dies einen Fehler verursacht. Auf die gleiche Weise weist kv3 der Eigenschaft value
eine Zahl zu, so dass der Compiler einen Fehler anzeigt. TypeScript verwendet also eine Schnittstelle, um die richtige Struktur eines Objekts zu gewährleisten.
Schnittstelle als Funktionstyp
Die TypeScript-Schnittstelle wird auch verwendet, um einen Typ einer Funktion zu definieren. Dadurch wird die Funktionssignatur sichergestellt.
Kopieren
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
Im obigen Beispiel enthält eine Schnittstelle KeyValueProcessor
eine Methodensignatur. Diese definiert den Funktionstyp. Jetzt können wir eine Variable vom Typ KeyValueProcessor
definieren, die nur auf Funktionen mit der gleichen Signatur verweisen kann, wie sie in der Schnittstelle KeyValueProcessor
definiert ist. Die Funktion addKeyValue
oder updateKeyValue
wird also kvp
zugewiesen. So kann kvp
wie eine Funktion aufgerufen werden.
Der Versuch, eine Funktion mit einer anderen Signatur zuzuweisen, führt zu einem Fehler.
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
Schnittstelle für Array-Typ
Eine Schnittstelle kann auch den Typ eines Arrays definieren, wobei man sowohl den Typ des Index als auch die Werte definieren kann.
Kopieren
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
Im obigen Beispiel definiert die Schnittstelle NumList
einen Typ eines Arrays mit Index als Zahl und Wert als Zahlentyp. Auf die gleiche Weise definiert IStringList
ein String-Array mit Index als String und Wert als String.
Optional Property
Manchmal kann es vorkommen, dass wir eine Schnittstelle mit einem Überschuss an Eigenschaften deklarieren, aber nicht von allen Objekten erwarten, dass sie alle gegebenen Eigenschaften der Schnittstelle definieren. Wir können optionale Eigenschaften haben, die mit einem „?“ gekennzeichnet sind. In solchen Fällen können die Objekte der Schnittstelle diese Eigenschaften definieren oder nicht.
Kopieren
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"}
Im obigen Beispiel ist empDept
mit ?
markiert, so dass Objekte von IEmployee
diese Eigenschaft enthalten können oder nicht.
Nur-Lesen-Eigenschaften
TypeScript bietet eine Möglichkeit, eine Eigenschaft als nur lesbar zu markieren. Das bedeutet, dass eine Eigenschaft, der einmal ein Wert zugewiesen wurde, nicht mehr geändert werden kann!
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
Im obigen Beispiel ist die Eigenschaft SSN
schreibgeschützt. Wir definieren das Objekt personObj vom Typ Citizen und weisen den beiden Schnittstelleneigenschaften Werte zu. Als Nächstes versuchen wir, die den beiden Eigenschaften name
und SSN
zugewiesenen Werte zu ändern. Der TypeScript-Compiler zeigt einen Fehler an, wenn wir versuchen, die nur lesbare Eigenschaft SSN
zu ändern.
Erweiterung von Schnittstellen
Schnittstellen können eine oder mehrere Schnittstellen erweitern. Das macht das Schreiben von Schnittstellen flexibel und wiederverwendbar.
Kopieren
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
In dem obigen Beispiel erweitert die Schnittstelle IEmployee
die Schnittstelle IPerson
. Daher müssen Objekte der Schnittstelle IEmployee
alle Eigenschaften und Methoden der Schnittstelle IPerson
enthalten, andernfalls zeigt der Compiler einen Fehler an.
Implementierung einer Schnittstelle
Ähnlich wie bei Sprachen wie Java und C# können Schnittstellen in TypeScript mit einer Klasse implementiert werden. Die Klasse, die die Schnittstelle implementiert, muss sich streng an die Struktur der Schnittstelle halten.
Kopieren
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");
Im obigen Beispiel wird die Schnittstelle IEmployee
in der Klasse Employee mit dem Schlüsselwort implement implementiert. Die implementierende Klasse sollte die Eigenschaften und die Funktion unbedingt mit demselben Namen und Datentyp definieren. Wenn die implementierende Klasse die Struktur nicht einhält, zeigt der Compiler einen Fehler an.
Natürlich kann die implementierende Klasse zusätzliche Eigenschaften und Methoden definieren, aber zumindest muss sie alle Mitglieder einer Schnittstelle definieren.
Im nächsten Kapitel werden wir mehr über TypeScript-Klassen erfahren.