Rozhraní je struktura, která definuje smlouvu ve vaší aplikaci. Definuje syntaxi, kterou se mají třídy řídit. Třídy, které jsou odvozeny z rozhraní, musí dodržovat strukturu danou jejich rozhraním.
Překladač jazyka TypeScript nepřevádí rozhraní do jazyka JavaScript. Pro kontrolu typů používá rozhraní. Tomu se také říká „kachní typování“ nebo „strukturální podtypování“.
Rozhraní se definuje pomocí klíčového slova interface
a může obsahovat deklarace vlastností a metod pomocí funkce nebo funkce se šipkou.
Kopírovat
interface IEmployee { empCode: number; empName: string; getSalary: (number) => number; // arrow function getManagerName(number): string; }
Ve výše uvedeném příkladu obsahuje rozhraní IEmployee
dvě vlastnosti empCode
a empName
. Dále obsahuje deklaraci metody getSalaray
využívající funkci arrow, která obsahuje jeden číselný parametr a číselný návratový typ. Metoda getManagerName
je deklarována pomocí normální funkce. To znamená, že každý objekt typu IEmployee
musí definovat tyto dvě vlastnosti a dvě metody.
Rozhraní jako typ
Rozhraní v jazyce TypeScript lze použít k definici typu a také k jeho implementaci ve třídě.
Následující rozhraní IEmployee
definuje typ proměnné.
Kopie
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:
Ve výše uvedeném příkladu rozhraní KeyPair
obsahuje dvě vlastnosti key
a value
. Proměnná kv1
je deklarována jako typ KeyPair
. Musí tedy mít stejnou strukturu jako KeyPair
. To znamená, že proměnné kv1
lze přiřadit pouze objekt s vlastnostmi key
typu číslo a value
typu řetězec. Překladač jazyka TypeScript zobrazí chybu, pokud dojde ke změně názvu vlastností nebo je datový typ jiný než KeyPair
. Další proměnná kv2
je také deklarována jako typ KeyPair
, ale přiřazená hodnota je val
místo value
, takže to způsobí chybu. Stejně tak kv3 přiřadí vlastnosti value
číslo, takže překladač zobrazí chybu. TypeScript tedy používá rozhraní, které zajišťuje správnou strukturu objektu.
Rozhraní jako typ funkce
Rozhraní jazyka TypeScript se také používá k definování typu funkce. Tím se zajistí signatura funkce.
Kopie
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
Ve výše uvedeném příkladu obsahuje rozhraní KeyValueProcessor
signaturu metody. Ta definuje typ funkce. Nyní můžeme definovat proměnnou typu KeyValueProcessor
, která může ukazovat pouze na funkce se stejnou signaturou, jaká je definována v rozhraní KeyValueProcessor
. Takže funkci addKeyValue
nebo updateKeyValue
přiřadíme funkci kvp
. Proměnnou kvp
lze tedy volat jako funkci.
Pokus o přiřazení funkce s jinou signaturou způsobí chybu.
function delete(key:number):void { console.log('Key deleted.')} let kvp: KeyValueProcessor = delete; //Compiler Error
Rozhraní pro typ pole
Rozhraní může také definovat typ pole, kde můžete definovat typ indexu i hodnoty.
Kopírovat
interface NumList { :number}let numArr: NumList = ;numArr;numArr;interface IStringList { :string}let strArr : IStringList;strArr = "TypeScript";strArr = "JavaScript";
Ve výše uvedeném příkladu definuje rozhraní NumList
typ pole s indexem jako číslo a hodnotou jako typ číslo. Stejně tak rozhraní IStringList
definuje pole typu string s indexem jako string a hodnotou jako string.
Volitelné vlastnosti
Někdy můžeme deklarovat rozhraní s nadbytečnými vlastnostmi, ale nemusíme očekávat, že všechny objekty budou definovat všechny dané vlastnosti rozhraní. Můžeme mít nepovinné vlastnosti, označené znakem „?“. V takových případech objekty rozhraní mohou, ale nemusí tyto vlastnosti definovat.
Kopírovat
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"}
V uvedeném příkladu je empDept
označen ?
, takže objekty IEmployee
mohou, ale nemusí tuto vlastnost obsahovat.
Vlastnosti pouze pro čtení
TypeScript poskytuje možnost označit vlastnost pouze pro čtení. To znamená, že jakmile je vlastnosti přiřazena hodnota, nelze ji měnit!
SSN
určena pouze pro čtení: Kopírovat interface Citizen { name: string; readonly SSN: number;}let personObj: Citizen = { SSN: 110555444, name: 'James Bond' }personObj.name = 'Steve Smith'; // OKpersonObj.SSN = '333666888'; // Compiler Error
V uvedeném příkladu je vlastnost SSN
určena pouze pro čtení. Definujeme objekt personObj typu Citizen a přiřadíme hodnoty dvěma vlastnostem rozhraní. Dále se pokusíme změnit hodnoty přiřazené oběma vlastnostem – name
a SSN
. Kompilátor jazyka TypeScript zobrazí chybu, když se pokusíme změnit vlastnost SSN
pouze pro čtení.
Rozšiřování rozhraní
Rozhraní mohou rozšiřovat jedno nebo více rozhraní. Díky tomu je psaní rozhraní flexibilní a opakovaně použitelné.
Kopírovat
interface IPerson { name: string; gender: string;}interface IEmployee extends IPerson { empCode: number;}let empObj:IEmployee = { empCode:1, name:"Bill", gender:"Male"}
V uvedeném příkladu rozšiřuje rozhraní IEmployee
rozhraní IPerson
. Objekty IEmployee
tedy musí obsahovat všechny vlastnosti a metody rozhraní IPerson
, jinak překladač zobrazí chybu.
Implementace rozhraní
Podobně jako v jazycích jako Java a C# lze rozhraní v jazyce TypeScript implementovat pomocí třídy. Třída implementující rozhraní musí striktně dodržovat strukturu rozhraní.
Kopírovat
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");
Ve výše uvedeném příkladu je rozhraní IEmployee
implementováno ve třídě Employee pomocí klíčového slova implement. Implementační třída by měla striktně definovat vlastnosti a funkce se stejným názvem a datovým typem. Pokud implementační třída nedodrží strukturu, pak překladač zobrazí chybu.
Implementační třída samozřejmě může definovat další vlastnosti a metody, ale přinejmenším musí definovat všechny členy rozhraní.
V příští kapitole se dozvíme více o třídách jazyka TypeScript.