ProgrammatieFullstack ontwikkelaar

Hoe werkt Exclude in TypeScript, wanneer het te gebruiken bij manipulaties met union-types, en welke nuances zijn er bij het werken met dit utility-type?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Exclude<T, U> is een utility type dat is verschenen in TypeScript voor het aftrekken van één type van een ander, wanneer het nodig is om bepaalde waarden uit een union type te verwijderen.

Geschiedenis van de vraag

Oorspronkelijk was er in TypeScript geen handige manier om een type van een ander af te trekken. Bij het creëren van generieke API's, evenals bij refactoring, was het vaak nodig om het "resterende" type te verkrijgen — alles behalve de verboden waarden. In plaats van handmatige manipulaties met union, moest men meerdere vergelijkbare interfaces ondersteunen.

Probleem

Bijvoorbeeld, wanneer er een type 'A | B | C' is, maar je moet het type zonder B krijgen. Dit is vaak nodig bij het opbouwen van complexe invoerparameters van functies, filtering van toegestane waarden en dynamische typevorming.

Oplossing

Exclude lost dit probleem op. De vereenvoudigde handtekening is als volgt:

type Exclude<T, U> = T extends U ? never : T;

Het retourneert een type dat alle leden van U uitsluit uit T.

Voorbeeld:

type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK

Belangrijke kenmerken:

  • Stelt in staat om dynamische types te vormen door delen uit een union af te trekken.
  • Vereenvoudigt refactoring — bij wijziging van het basistype worden alle afgeleiden automatisch bijgewerkt.
  • Kan worden gebruikt voor het filteren van switch-cases of object-sleutels.

Vragen met een valstrik.

Kan Exclude worden gebruikt voor gewone types en niet union?

Als T geen union-type is, maar in U zit — zal Exclude nog steeds werken, maar het resultaat kan never of T zijn, wat niet altijd intuïtief is.

Exclude<'a', 'a'> // resultaat: never Exclude<'a', 'b'> // resultaat: 'a'

Verwijdert Exclude alle vermeldingen van het type in de objectstructuur?

Nee, Exclude gaat niet recursief door geneste velden van het type, het sluit alleen op het hoogste niveau van de union uit.

Hoe werkt Exclude met interfaces en object types?

Het vergelijkt het hele type en niet afzonderlijke eigenschappen. Daarom verwijdert Exclude uit een union van verschillende interfaces alleen diegenen die volledig overeenkomen met U.

interface A { x: number }; interface B { y: string }; // Exclude<A|B, B> geeft: A (B komt volledig overeen)

Typefouten en anti-patronen

  • Pogingen om Exclude toe te passen voor geneste of gedeeltelijke overeenkomsten
  • Gebruik voor het "verwijderen" van eigenschappen van een interface en niet van union-opties
  • Negeren van de mogelijkheid om het type never te krijgen bij een volledige overeenkomst van types

Voorbeeld uit de praktijk

Negatieve case

Validatie van gebruikersrollen via Exclude<UserRoles, 'admin'>, maar vergeten dat Exclude niet wordt toegepast op geneste structuren — de rechten 'admin:sub' werden niet uitgesloten.

Voordelen:

  • Eenvoud van typevorming voor rollen.

Nadelen:

  • Onbekend gedrag met geneste of vergelijkbare types; een kritieke rol werd gemist.

Positieve case

Gebruik Exclude om de public API te beperken met acties: Exclude<Action, 'delete'>, wat de gevaarlijke operatie uitsluit.

Voordelen:

  • Veiligheid op het niveau van type-definitie, het is niet mogelijk om een verboden actie aan te roepen.

Nadelen:

  • Actuele lijsten van basis-types moeten worden onderhouden.