Interface er en struktur, der definerer kontrakten i din applikation. Den definerer den syntaks, som klasser skal følge. Klasser, der er afledt af en grænseflade, skal følge den struktur, som deres grænseflade giver.
TypeScript-kompileren konverterer ikke grænseflade til JavaScript. Den bruger interface til typekontrol. Dette er også kendt som “duck typing” eller “structural subtyping”.
En grænseflade defineres med nøgleordet interface
, og den kan indeholde egenskaber og metodeklarationer ved hjælp af en funktion eller en pilefunktion.
Kopier
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
I ovenstående eksempel omfatter IEmployee
-grænsefladen to egenskaber empCode
og empName
. Den indeholder også en metodeerklæring getSalaray
ved hjælp af en pilefunktion, der indeholder en talparameter og en talreturtype. Metoden getManagerName
er deklareret ved hjælp af en normal funktion. Det betyder, at ethvert objekt af typen IEmployee
skal definere de to egenskaber og de to metoder.
Interface som type
Interface i TypeScript kan bruges til at definere en type og også til at implementere den i klassen.
Den følgende grænseflade IEmployee
definerer en type af en variabel.
Kopier
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:
I ovenstående eksempel indeholder en grænseflade KeyPair
to egenskaber key
og value
. En variabel kv1
er erklæret som KeyPair
type. Så den skal følge den samme struktur som KeyPair
. Det betyder, at kun et objekt med egenskaberne key
af typen tal og value
af typen streng kan tildeles en variabel kv1
. TypeScript-kompileren vil vise en fejl, hvis der sker en ændring i navnet på egenskaberne, eller hvis datatypen er anderledes end KeyPair
. En anden variabel kv2
er også deklareret som KeyPair
type, men den tildelte værdi er val
i stedet for value
, så dette vil medføre en fejl. På samme måde tildeler kv3 et tal til egenskaben value
, så compileren vil vise en fejl. TypeScript bruger således en grænseflade til at sikre den korrekte struktur af et objekt.
Interface som funktionstype
TypeScript-interface bruges også til at definere en type for en funktion. Dette sikrer funktionssignaturen.
Kopier
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
I ovenstående eksempel indeholder en grænseflade KeyValueProcessor
en metodesignatur. Denne definerer funktionstypen. Nu kan vi definere en variabel af typen KeyValueProcessor
, som kun kan pege på funktioner med den samme signatur som defineret i grænsefladen KeyValueProcessor
. Så addKeyValue
eller updateKeyValue
-funktionen tildeles kvp
. Så kvp
kan kaldes som en funktion.
Vil du forsøge at tildele en funktion med en anden signatur, opstår der en fejl.
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
Interface for Array Type
En interface kan også definere typen af et array, hvor du kan definere typen af indeks samt værdier.
Kopier
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
I ovenstående eksempel definerer grænseflade NumList
en type af array med indeks som tal og værdi som taltype. På samme måde definerer IStringList
et array af strengtype med indeks som streng og værdi som streng.
Optionel egenskab
Nogle gange kan vi deklarere en grænseflade med overskydende egenskaber, men vi forventer måske ikke, at alle objekter definerer alle de givne grænsefladeegenskaber. Vi kan have valgfrie egenskaber, der er markeret med et “?”. I sådanne tilfælde kan objekter i grænsefladen definere disse egenskaber eller ej.
Kopier
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"}
I ovenstående eksempel er empDept
markeret med ?
, så objekter af IEmployee
kan eller kan ikke indeholde denne egenskab.
Nej læsebeskyttede egenskaber
TypeScript giver en måde at markere en egenskab som kun læsebeskyttet. Det betyder, at når først en egenskab er tildelt en værdi, kan den ikke ændres!
Kopier
interface Citizen { name: string; readonly SSN: number;}let personObj: Citizen = { SSN: 110555444, name: 'James Bond' }personObj.name = 'Steve Smith'; // OKpersonObj.SSN = '333666888'; // Compiler Error
I ovenstående eksempel er SSN
-egenskaben skrivebeskyttet. Vi definerer objektet personObj af typen Citizen og tildeler værdier til de to grænsefladeegenskaber. Dernæst forsøger vi at ændre de værdier, der er tildelt begge egenskaber-name
og SSN
. TypeScript-kompileren viser en fejl, når vi forsøger at ændre den skrivebeskyttede SSN
-egenskab.
Udvidelse af grænseflader
Interfaces kan udvide en eller flere grænseflader. Dette gør det fleksibelt og genanvendeligt at skrive grænseflader.
Kopier
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
I ovenstående eksempel udvider IEmployee
-grænsefladen IPerson
-grænsefladen. Så objekter af IEmployee
skal indeholde alle egenskaberne og metoderne i IPerson
-grænsefladen, ellers viser compileren en fejl.
Implementering af en grænseflade
Som i sprog som Java og C# kan grænseflader i TypeScript implementeres med en klasse. Klassen, der implementerer grænsefladen, skal strengt overholde strukturen for grænsefladen.
IEmployee
implementeret i klassen Employee ved hjælp af nøgleordet implement. Den implementerende klasse skal strengt definere egenskaberne og funktionen med samme navn og datatype. Hvis den implementerende klasse ikke følger strukturen, vil compileren vise en fejl.
Den implementerende klasse kan selvfølgelig definere ekstra egenskaber og metoder, men den skal i det mindste definere alle medlemmerne af en grænseflade.
I næste kapitel lærer vi mere om TypeScript-klasser.