Många utvecklare är förvirrade när de ska välja mellan ett TypeScript-gränssnitt och en typ. Detta beror förmodligen på att de är väldigt lika med små skillnader.
Låt oss utforska några förnuftiga metoder och standardvärden, samtidigt som vi avslöjar några dolda pärlor i TypeScript-språket och svarar på frågan ”Ska jag använda ett gränssnitt eller en typ?”
När du är klar kan du kolla in min andra artikel om TypeScript Interfaces vs Classes!
Här är min regel: För användningsfall som att skapa nya typer genom saker som primitiver, unionstyper och tupeltyper föredrar jag att använda nyckelordet type
. För allt annat (objekt/arrays) är det en interface
.
En interface
är extremt hjälpsam när man har att göra med datastrukturer eftersom de är en mycket visuell representation (även om type
också är det, detta är vanligtvis min preferens). Det är helt okej att välja en type
för dina objekt och arrays också.
Nåväl, låt oss avslöja lite mer om Types vs Interfaces i TypeScript så att du kan fatta ett mer välgrundat beslut.
Objects: Typ vs Interface
Typiskt sett skulle vi definiera en datastruktur för att modellera en typ mot våra avsedda variabler och funktionsargument. Oftast är en datastruktur en array eller ett objekt som kan innehålla några metoder.
Låt oss skapa ett objekt som ett gränssnitt (rekommenderat tillvägagångssätt):
interface Milkshake { name: string; price: number; getIngredients(): string;}
Här föredrar jag att använda ett gränssnitt eftersom det är tydligt att det är ett gränssnitt och inte en variabel med tilldelade data – en fin läsbarhetsvinst. Vi använder name
och price
för att möjliggöra inställning av poster och getIngredients
som vårt metodanrop.
🎉 Ladda ner den gratis!
Är du redo att gå längre än ForEach? Bli säker på avancerade metoder – Reduce, Find, Filter, Every, Some och Map.
- Förstå fullständigt hur du hanterar JavaScript-datastrukturer med oföränderliga operationer
- 31 sidor med djupgående syntax, verkliga exempel, tips och tricks
- Skriv renare och bättre strukturerad programmeringslogik inom 3 timmar
Som en extra bonus skickar vi dig också några extra godsaker över några extra e-postmeddelanden.
När vi jämför det med en typ – kan det lätt förväxlas med ett faktiskt objekt på grund av tilldelningen =
:
type Milkshake = { name: string; price: number; getIngredients(): string;};
Det här är bara min lilla preferens för att välja ett interface
framför att använda ett type
här – men det står dig fritt att använda nyckelordet type
om du vill.
Intersecting: Type vs Interface
Intersecting betyder helt enkelt att kombinera en eller flera typer! Detta mönster kan åstadkommas med hjälp av både ett interface
eller type
.
Låt oss anta följande gränssnitt, jag ska visa hur vi kan intersektera ett interface
och type
:
interface MilkshakeProps { name: string; price: number;}interface MilkshakeMethods { getIngredients(): string;}
Med ett gränssnitt skulle vi intersektera genom att använda nyckelordet extends
. Jag använder flera gränssnitt för att visa hur man utvidgar (ärver) till ett slutgiltigt Milkshake
-gränssnitt som innehåller alla egenskaper och metoder som definierats på annat håll:
// { name: string, price: number, getIngredients(): string }interface Milkshake extends MilkshakeProps, MilkshakeMethods {}
Vi kan nu använda Milkshake
var som helst och kan dra nytta av att ha name
, price
och getIngredients
i bara en gränssnittsreferens.
Här är hur vi skulle göra samma sak med en type
och skärning av typerna via &
:
// { name: string, price: number, getIngredients(): string }type Milkshake = MilkshakeProps & MilkshakeMethods;
Jag rekommenderar att man använder en type
i stället för en interface
när man vill skärning av typer. Att använda extends
känns lite mer spretigt och inte lika tydligt att läsa och jag känner att det är detta som nyckelordet type
är gjort för.
Det är också superenkelt att bara kombinera fler typer med type
. Jag tycker att koden är tydligare än om man använder extends
med integratorer.
Interface är också begränsat – aliaset type
kan användas för mer komplexa typer som tupler, primitivtyper, unioner och annat mer:
// { name: string, price: number, getIngredients(): string }type Milkshake = MilkshakeProps & { getIngredients(): string };
Primitivtyper: Typ vs. gränssnitt
Låt oss tala om primitiva typer. Ja, strängar, siffror osv. Du kan bara tilldela en primitiv typ till ett type
alias:
type MilkshakeName = string;interface Milkshake { name: MilkshakeName; price: number;}
Om du behöver använda en primitiv typ, använd ett type
. Gränssnitt är ett no-go här för bara ett enda värde eftersom syntaxen inte stöder det.
🎉 Ladda ner den gratis!
Är du redo att gå längre än ForEach? Bli säker på avancerade metoder – Reduce, Find, Filter, Every, Some och Map.
- Förstå fullständigt hur du hanterar JavaScript-datastrukturer med oföränderliga operationer
- 31 sidor med djupgående syntax, verkliga exempel, tips och tricks
- Skriv renare och bättre strukturerad programmeringslogik inom 3 timmar
Som en extra bonus skickar vi dig också några extra godsaker över några extra e-postmeddelanden.
För de flesta fall är det dock enklare att bara använda det primitiva värdet direkt:
interface Milkshake { name: string; price: number;}
Du ska inte överkomplicera din kod!
Klasser: Typ vs gränssnitt
Oavsett om du har valt ett type
eller interface
är sättet vi använder det med en klass detsamma:
type Size = { size: string};interface Milkshake { name: string; price: number; getIngredients(): string;}class Order implements Size, Milkshake { // Size size = 'large'; // Milkshake name = 'Vanilla'; price = 399; getIngredients() { return ; }}
Klasser har inte stöd för att implementera/utöka unionstyper, eftersom de anses vara statiska blueprints. Detta innebär att du måste vara väldigt explicit om varje typ du implementerar, eftersom den inte kan vara dynamisk eller ändras just nu på grund av TypeScript-begränsningar.
Lär dig mer om TypeScript Interfaces vs Classes!
Funktioner: Typ vs gränssnitt
Typiskt sett skulle jag skapa en funktion med hjälp av type
aliaset eftersom vi oftast vill skriva en anonym funktion:
type IngredientsFn = () => string;const getIngredients: IngredientsFn = () => ;
Men om du hellre vill använda en interface
för detta, så kan du göra det här:
interface IngredientsFn { () => string;}
Det känns bara lite konstigt jämfört med att använda type
, men återigen är båda helt giltiga TypeScript och det är ingen skillnad när koden kompileras.
När man inte ska använda en typ
Nu har vi utforskat de olika jämförelserna och rekommenderade tillvägagångssätten och det är dags att prata om Declaration Merging, en funktion i TypeScript som bara gäller interface
och som skulle vara en anledning att välja en interface
framför en type
.
För att slå samman typer med hjälp av type
skulle vi behöva göra något liknande och skapa en ny slutlig type
:
type MilkshakeProps = { name: string; price: number;};type MilkshakeMethods = { getIngredients(): string;};type Milkshake = MilkshakeProps & MilkshakeMethods;
Vår type Milkshake
är nu ett typalias av MilkshakeProps
och MilkshakeMethods
. Observera att för att uppnå detta beteende har vi varit tvungna att skapa Milkshake
, vilket ger oss tre individuella typalias.
Med gränssnitt finns det faktiskt ett smartare tillvägagångssätt (vissa kanske säger att det är lite för smart) som kallas declaration merging (sammanslagning av deklarationer).
Vi kan uppnå en deklarationssammanslagning genom att helt enkelt deklarera samma interface
två gånger i samma scope (detta kan vara antingen genom att importera gränssnittet från en annan modul, eller genom att deklarera det lokalt bredvid ett annat gränssnitt):
// declared once...interface Milkshake { name: string; price: number;}// declared again the same, it works!interface Milkshake { getIngredients(): string;}// { name: string, price: number, getIngredients(): string }const milkshake: Milkshake = { name: 'Banana', price: 399, getIngredients() {...}};
Milkshake innehåller nu name
, price
och getIngredients
! Men är det bra att slå ihop deklarationer? Jag är ärligt talat inget fan och jag känner att det kan leda till mer skada än nytta. Jag skulle komponera dina typer genom type
i stället för att använda declaration merging – men du vet åtminstone de viktigaste skillnaderna nu.
Slutsats
Så där har du det! De viktigaste skillnaderna mellan typer och gränssnitt i TypeScript.
För att sammanfatta, med några personliga preferenser också, skulle jag hålla mig till en interface
för objekt och använda nyckelordet type
alias för att komponera nya typer i farten. Dessa nya typer kan till och med vara från gränssnitt eller andra typer som tupler, unioner och intersektionstyper. Det finns många möjligheter, men genom att förstå tillvägagångssätt kan vi börja välja rätt verktyg.
För det mesta är gränssnitt och typer ganska lika förutom några få skillnader som behandlas här. Jag hoppas att du tyckte om att läsa!
Om du menar allvar med dina TypeScript-kunskaper är nästa steg att ta en titt på mina TypeScript-kurser, de kommer att lära dig hela språkets grunder i detalj samt många avancerade användningsområden som du behöver i den dagliga TypeScript-utvecklingen!
Happy coding!