Az interfész egy objektum alakja. Egy szabványos JavaScript objektum key:value
párok leképezése. A JavaScript objektum kulcsai szinte minden esetben karakterláncok, értékeik pedig bármilyen támogatott JavaScript értékek (primitív vagy absztrakt).
Egy interfész megmondja a TypeScript fordítónak, hogy egy objektumnak milyen tulajdonságnevei lehetnek, és a hozzájuk tartozó értéktípusok. Ezért az interfész egy típus, és absztrakt típus, mivel primitív típusokból áll.
Amikor egy objektumot tulajdonságokkal (kulcsokkal) és értékekkel definiálunk, a TypeScript implicit interfészt hoz létre az objektumban lévő tulajdonságnevek és azok értékeinek adattípusa alapján. Ez a típuskövetkeztetés miatt történik.

A fenti példában létrehoztunk egy student
objektumot firstName
, lastName
, age
és getSalary
mezőkkel, és hozzáadtunk néhány kezdeti értéket. Ezen információk felhasználásával a TypeScript létrehoz egy implicit interfész típust a student
számára.
{
firstName: string;
lastName: string;
age: number;
getSalary: (base: number) => number;
}
Az interfész olyan, mint egy objektum, de csak az objektum tulajdonságairól és azok típusairól tartalmaz információt. Létrehozhatunk interfész típust is, és adhatunk neki nevet, hogy használhassuk az objektumértékek megjegyzésére, de itt ennek az interfésznek nincs neve, mivel implicit módon jött létre. Összehasonlíthatja ezt az előző leckében szereplő függvénytípussal, amelyet először implicit módon hoztunk létre, majd explicit módon hoztunk létre egy függvénytípust a type alias használatával.
Próbáljunk meg szórakozni az objektum tulajdonságaival, miután definiáltuk.

Amint a fenti példából látható, a TypeScript megjegyzi egy objektum alakját, mivel a ross
típusa az implicit interfész. Ha megpróbáljuk egy tulajdonság értékét felülírni az interfészben megadottól eltérő típusú értékkel, vagy megpróbálunk egy új, az interfészben nem megadott tulajdonságot hozzáadni, akkor a TypeScript fordító nem fordítja le a programot.
Ha azt szeretnénk, hogy egy objektumnak alapvetően bármilyen tulajdonsága legyen, akkor explicit módon jelölhetünk egy any
értéket, és a TypeScript fordító nem fog következtetni a típusra a hozzárendelt objektum értékéből. Vannak más, jobb módszerek is, hogy pontosan ezt érjük el, és ezeket fogjuk végigvenni ebben a cikkben.

Interfész deklaráció
Az eddig látott implicit interfész ugyan technikailag egy típus, de nem volt explicit módon definiálva. Mint tárgyaltuk, egy interfész nem más, mint az a forma, amit egy objektum felvehet. Ha van egy függvényünk, amely elfogad egy argumentumot, amelynek objektumnak kellene lennie, de egy bizonyos alakúnak, akkor ezt az argumentumot (paramétert) egy interfész típussal kell annotálnunk.

A fenti példában definiáltunk egy getPersonInfo
függvényt, amely elfogad egy objektum argumentumot, amely firstName
, lastName
, age
és getSalary
meghatározott adattípusú mezőkkel rendelkezik. Vegyük észre, hogy a :<type>
annotációval egy olyan objektumot használtunk típusként, amely tartalmazza a tulajdonságneveket és a hozzájuk tartozó típusokat. Ez egy példa az anonim interfészre, mivel az interfésznek nincs neve, inline használtuk.
Az egész egy kicsit bonyolultnak tűnik a kezeléséhez. Ha az ross
objektum bonyolultabbá válik, és több helyen kell használni, a TypeScript csak egy olyan dolognak tűnik, amit kezdetben szerettél, de most már csak egy nehéz dolog kezelni. A probléma megoldására egy interfész típust definiálunk a interface
kulcsszóval.

A fenti példában egy objektum alakját leíró Person
interfészt definiáltunk, de ezúttal van egy név, amivel hivatkozhatunk erre a típusra. Ezt a típust használtuk a ross
változó, valamint a getPersonIfo
függvény person
argumentumának megjegyzésére. Ez tájékoztatja a TypeScriptet, hogy ezeket az entitásokat a Person
alakkal szemben érvényesítse.
Miért használjunk interfészt?
Az interfész típus fontos lehet egy adott alak érvényesítéséhez. Jellemzően a JavaScriptben futásidőben vakon bízunk abban, hogy egy objektum mindig tartalmazni fog egy adott tulajdonságot, és ennek a tulajdonságnak mindig egy adott típusú értéke lesz, mint például {age: 21, ...}
.
Amikor ténylegesen elkezdünk műveleteket végezni ezen a tulajdonságon anélkül, hogy először ellenőriznénk, hogy létezik-e az adott tulajdonság az objektumon, vagy hogy az értéke az-e, amit vártunk, a dolgok rosszul sülhetnek el, és ez utána használhatatlanná teheti az alkalmazást. Például {age: '21', ...}
, itt age
értéke egy string
.
Az interfészek biztonságos mechanizmust biztosítanak az ilyen forgatókönyvek kezelésére fordítási időben. Ha véletlenül olyan tulajdonságot használunk egy nem létező objektumon, vagy egy tulajdonság értékét használjuk illegális műveletben, a TypeScript fordító nem fordítja le a programunkat. Lássunk egy példát.

A fenti példában a printStudent
függvényen belül a _student
argumentum name
tulajdonságát próbáljuk használni. Mivel az _student
argumentum a Student
interfész típusa, a TypeScript fordító hibát dob a fordítás során, mivel ez a tulajdonság nem létezik a Student
interfészben.
Hasonlóképpen, a 100 — _student.firstName
nem érvényes művelet, mivel a firstName
tulajdonság a string
típusa, és amikor legutóbb ellenőriztem, nem lehet kivonni egy string
-et egy number
-ból a JavaScriptben (az eredmény NaN
).
A fenti példában a getSalary
mezőhöz a hagyományos módon írtunk függvénytípust. Használhatjuk azonban ugyanerre a test nélküli függvényszintaxist is, amit általában az interfészekben használunk.
interface Student {
firstName: string;
lastName: string;
age: number;
getSalary(base: number): number;
};
Optionális tulajdonságok
Néha szükség van arra, hogy egy objektum rendelkezzen egy olyan tulajdonsággal, amely egy adott adattípus adatait tartalmazza, de nem kötelező, hogy ez a tulajdonság az objektumon legyen. Ez hasonló az előző leckében megismert opcionális függvényparaméterekhez.
Az ilyen tulajdonságokat opcionális tulajdonságoknak nevezzük. Egy interfész tartalmazhat opcionális tulajdonságokat, és az opcionális függvényparaméterekhez hasonlóan a ?:Type
annotációt használjuk ezek ábrázolására.

A fenti példában a Student
interfész rendelkezik a age
tulajdonsággal, amely opcionális. Ha azonban a age
tulajdonságot megadjuk, akkor annak number
típusú értékkel kell rendelkeznie.
A ross
objektum esetében, amely a Student
interfész típusa, nem adtuk meg a age
tulajdonság értékét, ami legális, azonban a monica
esetében megadtuk a age
tulajdonságot, de annak értéke string
, ami nem legális. Ezért a TypeScript fordító hibát dob.
A hiba furcsának tűnhet, de valójában van értelme. Ha a age
tulajdonság nem létezik egy objektumon, akkor a object.age
undefined
értéket fog visszaadni, ami egy undefined
típusú. Ha létezik, akkor az értéknek number
típusúnak kell lennie.
Az age
tulajdonság értéke tehát vagy undefined
vagy number
típusú lehet, amit a TypeScriptben az union szintaxis number | undefined
segítségével ábrázolunk.
💡 A típusuniót egy Type System leckében fogjuk megtanulni.
Az opcionális tulajdonságok azonban komoly problémákat okoznak a program végrehajtása során. Képzeljük el, ha egy aritmetikai műveletben a age
tulajdonságot használjuk, de annak értéke undefined
. Ez egyfajta komoly probléma.
De a jó dolog az, hogy a TypeScript fordító nem engedi meg illegális műveletek végrehajtását egy opcionális tulajdonságon, mivel annak értéke undefined
lehet.

A fenti példában a age
tulajdonságon végzünk aritmetikai műveletet, ami illegális, mert ennek a tulajdonságnak az értéke a futási időben number
vagy undefined
lehet. A undefined
-n végzett aritmetikai műveletek eredménye NaN
(nem szám).
💡 A fenti programhoz azonban a
--strictNullChecks
flag-etfalse
-re kellett állítanunk, ami egy TypeScript fordítói flag. Ha megadjuk ezt az opciót, akkor a fenti program tökéletesen lefordítható.
Az ilyen hiba vagy figyelmeztetés elkerülése érdekében kifejezetten meg kell mondanunk a TypeScript fordítónak, hogy ez a tulajdonság number
típusú, és nem a number
vagy undefined
. Ehhez használjuk a type assertiont (AKA type conversion vagy typecasting).

A fenti programban a (_student.age as number)
-t használtuk, amely a _student.age
típusát number | undefined
-ről number
-ra konvertálja. Ez egy módja annak, hogy megmondjuk a TypeScript fordítónak, hogy “Hé, ez egy szám”. De jobb megoldás lenne, ha futásidőben is ellenőriznénk, hogy _student.age
undefined
-e, és ezután végeznénk el az aritmetikai műveletet.
💡 A típus-nyilatkozatokról egy Type System leckében fogunk tanulni.
Funkció típusa interfész használatával
Nem csak egy egyszerű objektum alakját, hanem egy interfész egy függvény szignatúráját is leírhatja. Az előző leckében a függvénytípus leírására a type alias-t használtuk, de az interfészek is képesek erre.
interface InterfaceName {
(param: Type): Type;
}
A szintaxis, amellyel egy interfészt függvénytípusként deklarálhatunk, hasonló magához a függvényaláíráshoz. Amint a fenti példából láthatjuk, az interfész teste pontosan egy névtelen függvény szignatúráját tartalmazza, természetesen a test nélkül. Itt a paraméternevek nem számítanak.

A fenti példában IsSumOdd
interfészt definiáltunk, amely egy olyan függvénytípust határoz meg, amely két number
típusú argumentumot fogad el és egy boolean
értéket ad vissza. Most már használhatjuk ezt a típust egy függvény leírására, mivel a IsSumOdd
interfész típus egyenértékű a (x: number, y: number) => boolean
függvénytípussal.
A névtelen metódusaláírással rendelkező interfész leír egy függvényt. De egy függvény a JavaScript birodalmában egy objektum is, ami azt jelenti, hogy ugyanúgy adhatunk tulajdonságokat egy függvény értékéhez, mint egy objektumhoz. Ezért teljesen legális, hogy bármilyen tulajdonságot definiálhatunk egy függvénytípusú interfészen.

A fenti példában a függvényt leíró IsSumOdd
interfészhez type
és calculate
tulajdonságokat adtunk. A Object.assign
módszerrel összevonjuk a type
és calculate
tulajdonságokat egy függvény értékével.
A függvény típusú interfészek hasznosak lehetnek a konstruktorfüggvények leírásához. A konstruktorfunkció hasonlít egy osztályhoz, amelynek feladata az objektumok (példányok) létrehozása. Az ES5-ig csak konstruktorfunkcióink voltak a class
JavaScriptben egy class
utánzására. Ezért a TypeScript az osztályokat konstruktorfüggvényekké fordítja, ha a ES5
vagy az alatti osztályokat célozza meg.
💡 Ha többet szeretne megtudni a konstruktorfüggvényről, kövesse ezt a cikket.
Ha az anonim függvény aláírás elé new
kulcsszót teszünk az interfészben, az konstruálhatóvá teszi a függvényt. Ez azt jelenti, hogy a függvényt csak az new
kulcsszó használatával lehet meghívni objektumok létrehozására, nem pedig hagyományos függvényhívással. Egy minta konstruktorfüggvény így néz ki:
function Animal( _name ) {
this.name = _name;
}var dog = new Animal( 'Tommy' );
console.log( dog.name ); // Tommy
Szerencsére nem kell konstruktorfüggvényekkel dolgoznunk, mivel a TypeScript biztosítja a class
kulcsszót egy osztály létrehozására, amivel sokkal egyszerűbb dolgozni, mint egy konstruktorfüggvénnyel, higgye el nekem. Valójában egy class
mélyen a JavaScriptben egy konstruktorfunkció. Próbálja ki az alábbi példát.
class Animal{
constructor( _name ) {
this.name = _name;
}
}console.log( typeof Animal ); // "function"
Az osztály és a konstruktorfunkció egy és ugyanaz a dolog. Az egyetlen különbség az, hogy a class
gazdag OOP-szintaxist biztosít számunkra. Ezért egy konstruktorfunkció típusú interfész egy osztályt képvisel.

A fenti példában a Animal
osztályt egy konstruktorfunkcióval definiáltuk, amely egy string
típusú argumentumot fogad el. Ezt tekinthetjük konstruktorfüggvénynek, amely a Animal
konstruktorhoz hasonló aláírással rendelkezik.
A AnimalInterface
konstruktorfüggvényt definiál, mivel a new
kulcsszóval ellátott anonymous function előtaggal rendelkezik. Ez azt jelenti, hogy a Animal
osztály a AnimalInterface
típusának minősül. Itt a AnimalInterface
interfész típusa egyenértékű a new (sound: string) => any
függvénytípussal.
A createAnimal
függvény ctor
AnimalInterface
típusú argumentumot fogad el, tehát átadhatjuk a Animal
osztályt argumentumértékként. A Animal
osztály getSound
metódusaláírását nem fogjuk tudni hozzáadni a AnimalInterface
osztályhoz, ennek okát az Osztályok leckében magyarázzuk el.
Indexálható típusok
Az indexálható objektum olyan objektum, amelynek tulajdonságaihoz indexaláírással lehet hozzáférni, mint a obj
. Ez az alapértelmezett módja egy tömbelem elérésének, de mi is megtehetjük ezt az objektum esetében.
var a = ;
var o = { one: 1, two: 2, three: 3 };console.log( a ); // 1
console.log( a ); // 1 (same as `a.one`)
Az objektumunknak időnként tetszőleges számú tulajdonsága lehet, anélkül, hogy határozott alakja lenne. Ebben az esetben egyszerűen használhatjuk a object
típust. Ez az object
típus azonban minden olyan értéket definiál, amely nem number
, string
, boolean
, symbol
, null
vagy undefined
, ahogy azt az alaptípusok leckében tárgyaltuk.
Ha szigorúan ellenőriznünk kell, hogy egy érték egyszerű JavaScript objektum-e, akkor gondban lehetünk. Ezt egy olyan interfésztípus segítségével oldhatjuk meg, amely a tulajdonság nevének index aláírásával rendelkezik.
interface SimpleObject {
: any;
}
A SimpleObject
interfész egy string
kulcsokkal rendelkező objektum alakját határozza meg, amelynek értékei any
adattípusúak lehetnek. Itt a key
tulajdonságnév csak helyőrzőnek szolgál, mivel szögletes zárójelbe van zárva.

A fenti példában ross
és monica
típusú SimpleObject
felületű objektumot definiáltunk. Mivel ezek az objektumok string
kulcsokat és any
adattípusú értékeket tartalmaznak, ez teljesen legális.
Ha a monica
-ban a number
típusú 1
kulcs miatt zavarban vagyunk, ez is legális, mivel a JavaScriptben az objektumok vagy tömbelemek number
vagy string
kulcsokkal indexelhetők, ahogy az alább látható.
var o = { 0: 'Zero', '1': 'One' };
var a = ;console.log( o ); // Zero
console.log( o ); // One
console.log( a ); // One
console.log( a ); // One
Ha pontosabban kell meghatározni a kulcsok és értékeik típusát, azt is biztosan meg tudjuk tenni. Például definiálhatunk egy indexelhető interfésztípust number
típusú kulcsokkal és number
típusú értékekkel, ha akarjuk.
💡 Egy indexaláírás kulcstípusának vagy
string
vagynumber
típusúnak kell lennie.

A fenti példában egy LapTimes
interfészt definiáltunk, amely number
típusú tulajdonságneveket és number
típusú értékeket tartalmazhat. Ez az interfész olyan adatstruktúrát reprezentálhat, amely number
kulcsokkal indexelhető, ezért a ross
tömb és a monica
és joey
objektumok legálisak.
A rachel
objektum azonban nem felel meg a LapTimes
alakjának, mivel a one
kulcs string
, és csak string
, például rachel
segítségével érhető el, semmi mással nem. Ezért a TypeScript fordító a fentiek szerint hibát fog dobni.
Egy indexelhető interfésztípusban lehetséges, hogy egyes tulajdonságok kötelezőek, mások pedig opcionálisak legyenek. Ez nagyon hasznos lehet, amikor egy objektumnak egy bizonyos alakra van szüksége, de igazából nem számít, ha extra és nem kívánt tulajdonságokat kapunk az objektumban.

A fenti példában egy LapTimes
interfészt definiáltunk, amelynek tartalmaznia kell a name
tulajdonságot string
értékkel és a age
opcionális tulajdonságot number
értékkel. Egy LapTimes
típusú objektumnak tetszőleges tulajdonságai is lehetnek, amelyeknek a kulcsa number
és az értéke is number
kell, hogy legyen.
A ross
objektum akkor is érvényes LapTimes
objektum, ha nem rendelkezik a age
tulajdonsággal, mivel az opcionális. A monica
objektum azonban rendelkezik a age
tulajdonsággal, de értéke string
, ezért nem felel meg a LapTimes
interfésznek.
A joey
objektum szintén nem felel meg a LapTimes
interfésznek, mivel rendelkezik egy gender
tulajdonsággal, amely egy string
típusú. A rachel
objektumnak nincs name
tulajdonsága, ami a LapTimes
interfészben szükséges.
💡 Van néhány gubanc, amire figyelnünk kell az indexelhető típusok használata során. Ezeket ebben a dokumentációban említjük.
Extending Interface
Az osztályokhoz hasonlóan egy interfész is örökölhet tulajdonságokat más interfészekből. Azonban a JavaScriptben az osztályokkal ellentétben egy interfész több interfészből is örökölhet. Egy interfész örökléséhez a extends
kulcsszót használjuk.

Az interfész kiterjesztésével a gyermek interfész megkapja a szülő interfész összes tulajdonságát. Ez nagyon hasznos, ha több interfésznek közös a szerkezete, és el akarjuk kerülni a kódduplikációt azáltal, hogy a közös tulajdonságokat kivesszük egy közös interfészbe, amely később örökölhető.

A fenti példában létrehoztunk egy Student
interfészt, amely a Person
és Player
interfész tulajdonságait örökli.
Multiple Interface Declarations
Az előző részben megtanultuk, hogyan örökölheti egy interfész egy másik interfész tulajdonságait. Ezt a extend
kulcsszó segítségével tettük meg. Ha azonban azonos nevű interfészeket ugyanabban a modulban (fájlban) deklarálunk, a TypeScript összevonja a tulajdonságaikat, mindaddig, amíg a tulajdonságneveik különböznek, vagy az ütköző tulajdonságtípusaik megegyeznek.

A fenti példában a Person
interfészt többször deklaráltuk. Ez egyetlen Person
interfészdeklarációt eredményez az összes Person
interfészdeklaráció tulajdonságainak összevonásával.
💡 Az Osztályok leckében megtanultuk, hogy egy
class
implicit módon deklarál egy interfészt, és egy interfész kiterjesztheti ezt az interfészt. Tehát ha egy programnak van egyPerson
osztálya és egyPerson
interfésze, akkor a végsőPerson
típus (interfész) az osztály és az interfész között összevont tulajdonságokkal rendelkezik.
Hálózati interfészek
Egy interfésznek lehetnek mélyen egymásba ágyazott struktúrái. Az alábbi példában az Student
interfész info
mezője egy firstName
és lastName
tulajdonságú objektum alakját határozza meg.

Hasonlóképpen teljesen legális, hogy egy interfész mezőjének egy másik interfész típusa legyen. A következő példában a Student
interfész info
mezőjének a típusa Person
interfész.
