TypeScript-interface versus type: Echte verschillen, getest met de compiler
Interfaces en typealiassen overlappen elkaar zozeer dat de keuze vaak willekeurig aanvoelt. Dit is wat de een kan doen en de ander niet, bevestigd door echte compilerfouten, plus een eenvoudige regel die uw team kan volgen.
- Voor gewone objectvormen zijn een interface en een typealias vrijwel uitwisselbaar.
- Alleen
typekan unies, tupels en andere niet-objecttypen benoemen. - Alleen
interfaceondersteunt het samenvoegen van declaraties, waarbij twee declaraties worden gecombineerd. - Een algemene regel: interfaces voor objectvormen
typevoor al het andere, consistent toegepast.
Weinig TypeScript-vragen veroorzaken zoveel discussie als TypeScript-interface versus type. Veel artikelen vermelden verschillen die er in oudere versies toe deden, maar nu verouderd zijn. Dus in plaats van beweringen te herhalen, hebben we ze getest.
We hebben één bestand geschreven waarin we elk verschil hebben gecontroleerd, en dit hebben we gecontroleerd met TypeScript 5.6.3 in de strikte modus. De hieronder genoemde compilerfouten zijn afkomstig van die run. Gedrag kan tussen versies veranderen, dus controleer het met de TypeScript-versie die uw project gebruikt. Als je nieuw bent in de taal, begin dan met onze TypeScript-tutorial voor beginners.
Het korte antwoord
Een interface beschrijft de vorm van een object en kan worden uitgebreid of heropend. Een type alias geeft een naam aan elk type, inclusief objecten, unies, tupels en functietypen. Waar beide kunnen werken, zit het verschil vooral in stijl.
Waar ze hetzelfde zijn
Deze twee verklaringen beschrijven identieke vormen:
interface User {
id: number;
name: string;
email?: string;
}
type UserAlias = {
id: number;
name: string;
email?: string;
};
const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a; // no error
TypeScript gebruikt structureel typen, wat betekent dat het vormen vergelijkt in plaats van namen. Daarom kan een User zonder klachten aan een UserAlias worden toegewezen. Beide ondersteunen optionele eigenschappen, readonly eigenschappen, methoden en indexhandtekeningen.
Interface versus type naast elkaar
| Functie | interface | typ |
|---|---|---|
| Objectvormen | Ja | Ja |
Vakbonden, zoals "a" | "b" |
Nee | Ja |
| Tupels en primitieven | Nee | Ja |
| Toegewezen en voorwaardelijke typen | Nee | Ja |
| Verklaring samenvoegen | Ja | Nee, het is een fout |
| Verlengen | strekt zich uit |
& kruispunt |
| Conflicterende eigenschappen | Fout bij de aangifte | Kan stilletjes never worden |
Gebruikt met implements |
Ja | Ja, als het een objecttype is |
Wat alleen typen kan doen
Veel nuttige typen zijn helemaal geen objectvormen, en alleen een typealias kan ze een naam geven:
type Status = "pending" | "paid" | "shipped"; // union
type Point = [number, number]; // tuple
type Handler = (event: string) => void; // function type
type Keys = keyof User; // computed type
type ReadonlyUser = Readonly<User>; // utility type
Union-typen behoren tot de meest waardevolle functies van TypeScript, omdat de compiler vervolgens elke waarde buiten de lijst afwijst:
let s: Status = "paid";
s = "refunded";
// error TS2322: Type '"refunded"' is not assignable to type 'Status'.
Hiermee worden typefouten en verouderde waarden opgespoord tijdens het compileren, voordat ze in productie gaan.
Verklaring samenvoegen
Als u een interface met dezelfde naam tweemaal declareert, voegt TypeScript deze samen tot één interface:
interface Settings {
theme: string;
}
interface Settings {
language: string;
}
const settings: Settings = { theme: "dark", language: "en" }; // no error
Probeer hetzelfde met een typealias, en de compiler weigert:
type Config = { theme: string };
type Config = { language: string };
// error TS2300: Duplicate identifier 'Config'.
Door samen te voegen kunt u met bibliotheken hun typen uitbreiden. U kunt bijvoorbeeld een aangepaste eigenschap toevoegen aan de globale Window-interface of aan het aanvraagobject van een raamwerk, zonder de bibliotheek te wijzigen. Dat is echt nuttig.
De keerzijde is dat fusies per ongeluk kunnen gebeuren. Als twee bestanden in een project een interface met dezelfde naam in hetzelfde bereik declareren, worden ze stilzwijgend gecombineerd, wat verwarrend kan zijn. Een typealias maakt van die botsing een fout.
verlengt versus kruispunten
Met beide kun je voortbouwen op een bestaand type:
interface Admin extends User {
permissions: string[];
}
type AdminAlias = UserAlias & { permissions: string[] };
Voor compatibele eigenschappen is het resultaat hetzelfde. Het verschil komt tot uiting in een conflict. Wanneer een interface het type van een eigenschap op incompatibele wijze probeert te wijzigen, rapporteert de compiler dit onmiddellijk:
interface Base { id: number }
interface Broken extends Base { id: string }
// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
// Types of property 'id' are incompatible.
// Type 'string' is not assignable to type 'number'.
Een kruispunt klaagt niet wanneer het wordt aangegeven. In plaats daarvan wordt de conflicterende eigenschap never, een type dat geen enkele waarde kan hebben. De fout verschijnt pas later, wanneer u deze probeert te gebruiken:
type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };
// error TS2322: Type 'number' is not assignable to type 'never'.
Die latere fout is nog verwarrender, omdat deze wegwijst van de feitelijke fout. Dit is een praktische reden om de voorkeur te geven aan extends bij het opbouwen van objecttypen.
Klassen en werktuigen
Een klasse kan beloven een vorm te matchen met behulp van implements, en dit werkt met een interface of een objecttype-alias:
class Account implements User {
constructor(public id: number, public name: string) {}
}
Deze is zonder fouten samengesteld. Merk op dat implements alleen controleert of de klasse overeenkomt. Er worden geen eigenschappen of code toegevoegd. Een klasse kan geen unietype implementeren, omdat er geen enkele vorm is die daarbij past.
Alleen-lezen en hulpprogrammatypen
Hulpprogrammatypen zoals Readonly, Partial, Pick en Omit werken met zowel interfaces als aliassen. Het resultaat is een nieuw type, dat doorgaans wordt benoemd met een typealias:
const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";
// error TS2540: Cannot assign to 'name' because it is a read-only property.
Houd er rekening mee dat readonly alleen een controle tijdens het compileren is. Tijdens runtime is het object gewoon JavaScript en kan het nog steeds worden gewijzigd door code die de typen omzeilt.
Tip: Typen verdwijnen wanneer TypeScript naar JavaScript compileert. Gegevens die van buitenaf binnenkomen, zoals een API-antwoord, worden tijdens runtime niet gecontroleerd alleen omdat ze een type hebben. Valideer het bijvoorbeeld met een schemabibliotheek. In onze API-tutorial voor ophalen wordt uitgelegd hoe u veilig met API-reacties omgaat.
Compilerprestaties
De eigen prestatierichtlijnen van het TypeScript-team suggereren dat de voorkeur wordt gegeven aan interfaces die extends gebruiken boven grote kruispunten, omdat de compiler interfacerelaties efficiënter in de cache kan opslaan. Bij kleine en middelgrote projecten is het onwaarschijnlijk dat u enig verschil merkt. Het kan ertoe doen in zeer grote codebases met diep samengestelde typen. Wij hebben dit zelf niet gemeten.
Welke moet je gebruiken?
Een veelgebruikte regel, in lijn met de richtlijnen van het TypeScript-handboek, is:
- Gebruik
interfacevoor objectvormen, vooral openbare API’s en alles wat klassen implementeren of door anderen kunnen worden uitgebreid. - Gebruik
typevoor verbindingen, tupels, functietypen en toegewezen of berekende typen.
| Jij definieert | Gebruik |
|---|---|
| Een gegevensmodel, zoals Gebruiker of Bestelling | interface |
| Een reeks toegestane waarden | type unie |
| Een paar met vaste lengte of een triple | type tupel |
| Een terugbelhandtekening | typ |
| Uitbreiding van de typen van een bibliotheek | interface, door samenvoeging |
| Een variatie op een bestaand type | type met een nutstype |
Sommige teams gebruiken type gewoon overal, en dat werkt ook prima. Het belangrijkste is consistentie, dus kies een conventie, schrijf deze in je stijlgids en laat een linter deze afdwingen. ESLint’s consistent-type-definitions regel van typescript-eslint kan dit doen.
Veel voorkomende fouten
- Eindeloze discussies. Beide zijn prima voor objecten, dus bepaal de regel één keer en ga verder.
- Per ongeluk samenvoegen. Door een interfacenaam binnen hetzelfde bereik opnieuw te gebruiken, worden deze op de achtergrond gecombineerd.
- Conflicterende kruispunten. Ze creëren
nevertypen met later verwarrende fouten. - Vertrouwde typen tijdens runtime. Ze worden gewist, dus valideer externe gegevens.
- Gebruik
anyom fouten uit te sluiten. De controle wordt uitgeschakeld. Geef de voorkeur aanunknownen beperk het.
Ertussen schakelen in een bestaande codebase
Het veranderen van een interface in een type, of omgekeerd, is meestal veilig voor gewone objectvormen, omdat TypeScript typen vergelijkt op basis van hun structuur, niet op basis van hun naam. Er zijn nog een aantal zaken die je moet controleren voordat je overstapt:
- Door een interface naar een typealias te converteren, wordt het samenvoegen van declaraties verwijderd, zodat elke tweede declaratie met dezelfde naam een dubbele identificatiefout wordt.
- Het converteren van een typealias naar een interface werkt alleen voor objectvormen. Vakbonden, tupels en toegewezen typen moeten als typealiassen blijven.
- Foutmeldingen en hovers van de editor kunnen het type anders weergeven, wat teamgenoten kan verrassen maar het gedrag niet verandert.
Voer de wijziging door in één gerichte pull-request, voer de compiler uit in de strikte modus en uw testpakket, en vermijd het vermengen ervan met functiewerk, zodat reviewers kunnen zien dat er verder niets is veranderd.
Tot slot
De interface versus type-vraag heeft een rustig antwoord: ze overlappen elkaar voor objecten, elk heeft een paar unieke vaardigheden, en consistentie is belangrijker dan de keuze. Gebruik interfaces voor vormen en typen voor al het andere, tenzij uw team anders verkiest. Om zelf de verschillen te zien, kopieert u de bovenstaande voorbeelden naar een bestand en voert u daarop tsc --noEmit --strict uit.
Veelgestelde vragen
Wat is het verschil tussen interface en type in TypeScript?
Beide kunnen objectvormen beschrijven. Alleen het type kan unies, tupels en berekende typen een naam geven, terwijl alleen de interface het samenvoegen van declaraties ondersteunt.
Moet ik interface of type gebruiken voor React-rekwisieten?
Beide werken goed. Veel teams gebruiken interfaces voor rekwisieten, terwijl anderen de voorkeur geven aan type vanwege consistentie met vakbonden. Kies er één en pas deze gedurende het hele project toe.
Kan een type een interface uitbreiden?
Ja, door een kruispunt te gebruiken zoals type Admin = Gebruiker & { permissies: string[] }. Een interface kan ook een objecttype-alias uitbreiden.
Is de interface sneller dan het type?
De prestatierichtlijnen van TypeScript geven de voorkeur aan een interface die zich uitstrekt over grote kruispunten in grote codebases. Bij typische projecten is het onwaarschijnlijk dat het verschil merkbaar is.
Wat is het samenvoegen van aangiften?
Het is wanneer twee interfaces met dezelfde naam in hetzelfde bereik worden gecombineerd tot één. Type-aliassen kunnen niet worden samengevoegd en dubbele namen veroorzaken een fout.
Blijf leren
Hulp nodig met testen, WordPress of SEO?
We helpen teams betere software te leveren en hun sites te laten groeien. Vertel ons waar u mee bezig bent en wij stellen een duidelijke volgende stap voor.
Meer handleidingen over testen, AI en moderne ontwikkeling
Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.
AI-hallucinaties uitgelegd: waarom taalmodellen dingen verzinnen en hoe ze te verminderen
AI-hallucinaties uitgelegd: wat ze zijn, waarom taalmodellen ze produceren, veelvoorkomende typen, hoe je ze kunt verminderen met aarding en aanwijz…
Lees artikel →DevOpsOmgevingsvariabelen uitgelegd: .env-bestanden, geheimen en configuratie goed gedaan
Omgevingsvariabelen uitgelegd met Python-voorbeelden die we hebben uitgevoerd: ze lezen en instellen, waarom elke waarde een string is, .env-bestand…
Lees artikel →SoftwareontwikkelingAPI-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten
API-snelheidsbeperking uitgelegd met een simulatie die we hebben uitgevoerd: token-bucket, vast venster, schuifvenster, 429 reacties, headers van sn…
Lees artikel →TestautomatiseringSpotten in Python: unittest.mock, patch en de fouten die tests doen liegen
Spotten in Python met unittest.mock, getest: Mock en return_value, side_effect, patchen waar een naam wordt gebruikt, spec en autospec, spotdatums e…
Lees artikel →SoftwaretestenCodedekking uitgelegd: lijn versus filiaaldekking en wat 100% werkelijk betekent
Codedekking uitgelegd met een echte dekking.py-run: dekking van lijnen, vertakkingen en condities, een rapport lezen, waarom 100% dekking nog steeds…
Lees artikel →Databases & SQLDELETE versus TRUNCATE versus DROP in SQL: verschillen, terugdraaien en wanneer elk te gebruiken
DELETE versus TRUNCATE versus DROP uitgelegd met geteste SQL: wat elk verwijdert, terugdraaien, triggers, identiteitsreset, snelheid en hoe PostgreS…
Lees artikel →Databases & SQLSQL-weergaven uitgelegd: creëer, gebruik en vermijd de valkuilen, met voorbeelden
SQL-weergaven uitgelegd met query's die we hebben uitgevoerd: een weergave maken, live resultaten, kolommen verbergen, prestaties, bijwerkbare en ge…
Lees artikel →SoftwareontwikkelingTypeScript-interface versus type: echte verschillen, getest met de compiler
TypeScript-interface versus type, gecontroleerd met de echte compiler: wat beide kunnen doen, wat slechts één kan doen, samenvoegen van declaraties,…
Lees artikel →SoftwareontwikkelingPython *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden
Python *args en **kwargs uitgelegd met code die we hebben uitgevoerd: in- en uitpakken, parametervolgorde, argumenten die alleen trefwoorden en posi…
Lees artikel →SoftwareontwikkelingPython Lambda-functies: syntaxis, echt gebruik en wanneer u ze niet moet gebruiken
Python lambda-functies uitgelegd met code die we hebben uitgevoerd: syntaxis, sorteren met sleutel, kaart en filter, sluitingen, de laat-bindende va…
Lees artikel →Kunstmatige intelligentieWat zijn AI-agenten? Hoe ze werken, met een eenvoudige agentloop die u kunt uitvoeren
Wat zijn AI-agenten? Een duidelijke gids voor de agentenlus, tools, geheugen en vangrails, met een kleine Python-lus die we hebben uitgevoerd, echte…
Lees artikel →DevOpsMonitoring versus waarneembaarheid: het echte verschil, met logboeken, statistieken en sporen
Monitoring versus waarneembaarheid uitgelegd: bekende en onbekende fouten, logboeken, statistieken en sporen, een walkthrough voor afrekenincidenten…
Lees artikel →DevOpsDocker-volumes versus bindingsmounts: bewaar containergegevens op de juiste manier
Docker-volumes versus bind-mounts uitgelegd: waarom containergegevens verdwijnen, benoemde volumes, bind-mounts, tmpfs, Compose-voorbeelden, back-up…
Lees artikel →TestautomatiseringTDD versus BDD: belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt
TDD versus BDD uitgelegd met één kortingsfunctie die in beide richtingen is gebouwd: rood-groen-refactor met code die we hebben uitgevoerd, gegeven-…
Lees artikel →SoftwareontwikkelingGit merge vs rebase: verschillen, voorbeelden en wanneer je wat gebruikt
Git merge vs rebase uitgelegd met echte logoutput, een vergelijkingstabel, de gouden regel van rebase en een veilige teamwerkwijze.
Lees artikel →SoftwareontwikkelingPython list comprehension: syntaxis, voorbeelden en wanneer je het vermijdt
Python list comprehension uitgelegd: syntaxis, filteren, if-else, geneste lussen, gemeten tijden en wanneer een gewone for-lus beter is.
Lees artikel →Databases & SQLSQL JOIN uitgelegd met voorbeelden (Inner, Left, Right, Full)
Begrijp SQL joins stap voor stap met voorbeeldtabellen en queries: INNER, LEFT, RIGHT, FULL, CROSS en SELF, plus veelgemaakte fouten en prestatietips.
Lees artikel →