Rajapinta on rakenne, joka määrittelee sovelluksesi sopimuksen. Se määrittelee syntaksin, jota luokat noudattavat. Rajapinnasta johdettujen luokkien on noudatettava rajapintansa antamaa rakennetta.
TypeScript-kääntäjä ei muunna rajapintaa JavaScriptiksi. Se käyttää rajapintaa tyypintarkistukseen. Tämä tunnetaan myös nimellä ”ankkatyypitys” tai ”rakenteellinen alatyypitys”.
Liittymä määritellään avainsanalla interface
, ja se voi sisältää ominaisuuksien ja metodien julistuksia funktiota tai nuolifunktiota käyttäen.
Kopioi
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
Yllä olevassa esimerkissä IEmployee
-rajapinta sisältää kaksi ominaisuutta empCode
ja empName
. Se sisältää myös metodi-ilmoituksen getSalaray
, jossa käytetään nuolifunktiota, joka sisältää yhden numeroparametrin ja numeron paluutyypin. Metodi getManagerName
on deklaroitu käyttäen normaalia funktiota. Tämä tarkoittaa, että minkä tahansa IEmployee
-tyyppisen objektin on määriteltävä nämä kaksi ominaisuutta ja kaksi metodia.
Interfacen käyttäminen tyyppinä
Interfacen avulla TypeScriptissä voidaan määritellä tyyppi ja myös toteuttaa se luokassa.
Seuraava rajapinta IEmployee
määrittelee muuttujan tyypin.
Kopioi
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:
Yllä olevassa esimerkissä rajapinta KeyPair
sisältää kaksi ominaisuutta key
ja value
. Muuttuja kv1
on ilmoitettu tyypiksi KeyPair
. Sen on siis noudatettava samaa rakennetta kuin KeyPair
. Se tarkoittaa, että muuttujaan kv1
voidaan liittää vain objekti, jonka ominaisuudet key
ovat numerotyyppisiä ja value
merkkijonotyyppisiä. TypeScript-kääntäjä näyttää virheen, jos ominaisuuksien nimet muuttuvat tai tietotyyppi on eri kuin KeyPair
. Toinen muuttuja kv2
on myös ilmoitettu KeyPair
-tyyppiseksi, mutta sille annettu arvo on val
eikä value
, joten tämä aiheuttaa virheen. Samalla tavalla kv3 määrittää ominaisuudelle value
numeron, joten kääntäjä näyttää virheen. TypeScript käyttää siis rajapintaa varmistaakseen objektin oikean rakenteen.
Liittymä funktion tyyppinä
TypeScriptin rajapintaa käytetään myös funktion tyypin määrittelyyn. Näin varmistetaan funktion allekirjoitus.
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
Yllä olevassa esimerkissä rajapinta KeyValueProcessor
sisältää metodin allekirjoituksen. Tämä määrittelee funktion tyypin. Nyt voimme määritellä muuttujan, jonka tyyppi on KeyValueProcessor
ja joka voi osoittaa vain funktioihin, joiden allekirjoitus on sama kuin rajapinnassa KeyValueProcessor
määritelty. Eli addKeyValue
– tai updateKeyValue
-funktio osoitetaan kvp
:lle. Joten kvp
voidaan kutsua kuin funktiota.
Yritetään osoittaa funktio, jolla on eri allekirjoitus, aiheuttaa virheen.
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
Interfacen käyttöliittymä array-tyypille
Interfacen avulla voidaan määritellä myös array-tyyppi, jossa voidaan määritellä indeksin tyyppi sekä arvot.
Copy
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
Yllä olevassa esimerkissä rajapinta NumList
määrittelee tyypin array, jonka indeksi on numero ja arvo on numerotyyppi. Samalla tavalla IStringList
määrittelee merkkijonomäärän, jonka indeksi on merkkijono ja arvo merkkijono.
Optional Property
Joskus saatamme ilmoittaa rajapinnan, jolla on liikaa ominaisuuksia, mutta emme välttämättä odota kaikkien objektien määrittelevän kaikkia annettuja rajapinnan ominaisuuksia. Meillä voi olla valinnaisia ominaisuuksia, jotka on merkitty ”?”-merkillä. Tällaisissa tapauksissa rajapinnan objektit voivat määritellä tai olla määrittelemättä näitä ominaisuuksia.
Kopioi
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"}
Yllä olevassa esimerkissä empDept
on merkitty ?
:lla, joten IEmployee
:n objektit voivat sisältää tai olla sisällyttämättä tätä ominaisuutta.
Ominaisuudet, jotka on tarkoitettu vain luettaviksi
TypeScript tarjoaa tavan merkitä ominaisuus vain luettavaksi. Tämä tarkoittaa, että kun ominaisuudelle on annettu arvo, sitä ei voi muuttaa!
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
Yllä olevassa esimerkissä SSN
-ominaisuus on read only. Määrittelemme personObj-olion tyypin Citizen ja annamme kahdelle rajapintaominaisuudelle arvot. Seuraavaksi yritämme muuttaa molemmille ominaisuuksille – name
ja SSN
– annettuja arvoja. TypeScript-kääntäjä näyttää virheen, kun yritämme muuttaa vain lukuarvoa sisältävää SSN
-ominaisuutta.
Rajapintojen laajentaminen
Rajapinnat voivat laajentaa yhtä tai useampaa rajapintaa. Tämä tekee rajapintojen kirjoittamisesta joustavaa ja uudelleenkäytettävää.
Copy
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
Yllä olevassa esimerkissä IEmployee
-rajapinta laajentaa IPerson
-rajapintaa. Niinpä IEmployee
:n objektien on sisällettävä kaikki IPerson
-rajapinnan ominaisuudet ja metodit, muuten kääntäjä näyttää virheen.
Rajapinnan toteuttaminen
Samankaltaisesti kuin Javan ja C#:n kaltaisissa kielissä, TypeScriptissä rajapinnat voidaan toteuttaa luokan avulla. Rajapinnan toteuttavan luokan on noudatettava tiukasti rajapinnan rakennetta.
Kopioi
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");
Yllä olevassa esimerkissä IEmployee
rajapinta on toteutettu Employee-luokassa implement-avainsanalla. Toteuttavan luokan tulee ehdottomasti määritellä ominaisuudet ja funktio samalla nimellä ja tietotyypillä. Jos toteuttava luokka ei noudata rakennetta, kääntäjä näyttää virheen.
Tietysti toteuttava luokka voi määritellä ylimääräisiä ominaisuuksia ja metodeja, mutta ainakin sen on määriteltävä kaikki rajapinnan jäsenet.
Seuraavassa luvussa opimme lisää TypeScript-luokista.