Mange udviklere er forvirrede, når de skal vælge mellem en TypeScript-interface eller en type. Det skyldes sandsynligvis, at de er meget ens med mindre forskelle.
Lad os udforske nogle fornuftige fremgangsmåder og standardindstillinger, mens vi afdækker nogle skjulte perler i TypeScript-sproget og besvarer spørgsmålet “Skal jeg bruge et interface eller en type?”
Når du er færdig, så tjek min anden artikel om TypeScript Interfaces vs Classes!
Her er min regel: For use cases såsom oprettelse af nye typer gennem ting som primitives, unionstyper og tupletyper, foretrækker jeg at bruge nøgleordet type
. For alt andet (objekter/arrays) er det en interface
.
En interface
er ekstremt nyttig når man har med datastrukturer at gøre, da de er en meget visuel repræsentation (om end det også er type
, dette er typisk min præference). Det er også helt i orden at vælge en type
til dine objekter og arrays.
Nuvel, lad os dog afdække lidt mere om typer vs. interfaces i TypeScript, så du kan træffe en mere informeret beslutning.
Objekter: Type vs Interface
Typisk ville vi definere en datastruktur til at modellere en type i forhold til vores tilsigtede variabler og funktionsargumenter. Oftest er en datastruktur et array eller et objekt, der måske indeholder et par metoder.
Lader os oprette et objekt som en grænseflade (anbefalet fremgangsmåde):
interface Milkshake { name: string; price: number; getIngredients(): string;}
Her foretrækker jeg at bruge en grænseflade, fordi det er tydeligt, at det er en grænseflade og ikke en variabel med data tildelt – en dejlig læsbarhedsgevinst. Vi bruger name
og price
til at tillade indstilling af poster og getIngredients
som vores metodekald.
🎉 Download den gratis!
Er du klar til at gå videre end ForEach? Bliv fortrolig med avancerede metoder – Reduce, Find, Filter, Every, Some og Map.
- Få fuld forståelse for, hvordan du håndterer JavaScript-datastrukturer med uforanderlige operationer
- 31 sider med dybdegående syntaks, eksempler fra den virkelige verden, tips og tricks
- Skriv renere og bedre struktureret programmeringslogik inden for 3 timer
Som en ekstra bonus sender vi dig også nogle ekstra godbidder på tværs af et par ekstra e-mails.
Når vi sammenligner det med en type – kan det nemt forveksles med et egentligt objekt på grund af tildelingen =
:
type Milkshake = { name: string; price: number; getIngredients(): string;};
Det er bare min lille præference for at vælge en interface
frem for at bruge en type
her – men du er velkommen til at bruge nøgleordet type
, hvis du ønsker det.
Intersecting: Type vs Interface
Intersecting betyder simpelthen at kombinere en eller flere typer! Dette mønster kan opnås ved hjælp af både en interface
eller type
.
Lad os antage følgende grænseflader, jeg vil vise dig, hvordan vi kan intersecte en interface
og type
:
interface MilkshakeProps { name: string; price: number;}interface MilkshakeMethods { getIngredients(): string;}
Med en grænseflade ville vi intersecte ved at bruge nøgleordet extends
. Jeg bruger flere grænseflader for at vise, hvordan man udvider (arver) til en endelig Milkshake
grænseflade, der indeholder alle egenskaber og metoder, som er defineret andre steder:
// { name: string, price: number, getIngredients(): string }interface Milkshake extends MilkshakeProps, MilkshakeMethods {}
Vi kan nu bruge Milkshake
hvor som helst og kan drage fordel af at have name
, price
og getIngredients
i blot én grænsefladereference.
Her er hvordan vi ville gøre det samme med en type
og skære typerne via &
:
// { name: string, price: number, getIngredients(): string }type Milkshake = MilkshakeProps & MilkshakeMethods;
Jeg vil anbefale at bruge en type
i stedet for en interface
, når du vil skære typer. At bruge extends
føles lidt mere mundret og ikke så overskueligt at læse, og jeg føler, at det er det, som type
-keywordet er lavet til.
Det er også super nemt bare at kombinere flere typer med type
. Jeg synes, at koden er mere overskuelig end at bruge extends
med intefaces.
Interfaces er også begrænset – type
aliaset kan bruges til mere komplekse typer som tupler, primitives, unions og andre mere:
// { name: string, price: number, getIngredients(): string }type Milkshake = MilkshakeProps & { getIngredients(): string };
Primitives: Type vs. grænseflade
Lad os tale om primitive typer. Ja, strings, tal osv. Du kan kun tildele en primitiv type til et type
alias:
type MilkshakeName = string;interface Milkshake { name: MilkshakeName; price: number;}
Hvis du skal bruge en primitiv type, skal du bruge et type
. Interfaces er et no-go her for bare en enkelt værdi, da syntaksen ikke understøtter det.
🎉 Download den gratis!
Er du klar til at gå videre end ForEach? Bliv fortrolig med avancerede metoder – Reduce, Find, Filter, Every, Some og Map.
- Få fuld forståelse for, hvordan du håndterer JavaScript-datastrukturer med uforanderlige operationer
- 31 sider med dybdegående syntaks, eksempler fra den virkelige verden, tips og tricks
- Skriv renere og bedre struktureret programmeringslogik inden for 3 timer
Som en ekstra bonus sender vi dig også nogle ekstra godbidder på tværs af et par ekstra e-mails.
For de fleste tilfælde vil det dog være nemmere bare at bruge den primitive værdi direkte:
interface Milkshake { name: string; price: number;}
Du skal ikke overkomplicere din kode!
Klasser: Type vs. grænseflade
Hvorvidt du har valgt en type
eller interface
, er måden vi bruger den på med en klasse den samme:
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 understøtter ikke implementering/udvidelse af unionstyper, fordi de anses for at være statiske blueprints. Det betyder, at du skal være super eksplicit omkring hver type, du implementerer, da den ikke kan være dynamisk eller ændres lige nu på grund af TypeScript-begrænsninger.
Læs mere om TypeScript Interfaces vs. Classes!
Funktioner: Type vs Interface
Typisk ville jeg oprette en funktion ved hjælp af type
aliaset, da vi oftest ønsker at skrive en anonym funktion:
type IngredientsFn = () => string;const getIngredients: IngredientsFn = () => ;
Hvis det imidlertid er mere din stil at bruge en interface
til dette, så kan du se her, hvordan du gør det:
interface IngredientsFn { () => string;}
Det føles bare lidt skævt i forhold til at bruge type
, men igen er begge dele fuldstændig gyldigt TypeScript, og der er ingen forskel, når koden kompileres.
Når man ikke skal bruge en type
Nu har vi udforsket de forskellige sammenligninger og anbefalede fremgangsmåder, og det er tid til at tale om Declaration Merging, en funktion i TypeScript, der kun gælder for interface
, og som ville være en grund til at vælge en interface
frem for en type
.
For at flette typer ved hjælp af type
ville vi skulle gøre noget i stil med dette og oprette en ny final type
:
type MilkshakeProps = { name: string; price: number;};type MilkshakeMethods = { getIngredients(): string;};type Milkshake = MilkshakeProps & MilkshakeMethods;
Vores type Milkshake
er nu et typealias af MilkshakeProps
og MilkshakeMethods
. Bemærk, at for at opnå denne adfærd har vi været nødt til at oprette Milkshake
, hvilket giver os 3 individuelle typealiaser.
Med interfaces er der faktisk en smartere fremgangsmåde (nogle vil måske sige lidt for smart), der kaldes declaration merging.
Vi kan opnå en deklarationssammenlægning ved simpelthen at deklarere den samme interface
to gange i samme scope (det kan enten være via import af grænsefladen fra et andet modul eller ved at deklarere den lokalt ved siden af en anden grænseflade):
// 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 indeholder nu name
, price
og getIngredients
! Men er sammenlægning af deklarationer en god ting? Jeg er ærligt talt ikke fan, og jeg føler, at det kan føre til mere skade end gavn. Jeg ville sammensætte dine typer gennem type
i stedet for at bruge declaration merging – men du kender i det mindste den eller de vigtigste forskelle nu.
Conclusion
Så der har du det! De vigtigste forskelle mellem typer og interfaces i TypeScript.
For at opsummere, også med nogle personlige præferencer, ville jeg holde mig til en interface
for objekter og bruge type
alias-keywordet til at sammensætte nye typer i farten. Disse nye typer kunne endda være fra interfaces eller andre typer såsom tupler, unioner og intersektionstyper. Der er mange muligheder, men ved at forstå fremgangsmåder kan vi begynde at vælge det rigtige værktøj.
For det meste er interfaces og typer stort set det samme bortset fra nogle få forskelle, der er dækket her. Jeg håber du nød at læse!
Hvis du er seriøs omkring dine TypeScript færdigheder, er dit næste skridt at tage et kig på mine TypeScript kurser, de vil lære dig alle sprogets grundprincipper i detaljer samt mange avancerede use cases du vil få brug for i den daglige TypeScript udvikling!
Happy coding!