Gemerkt Types in TypeScript

Wanneer je entiteiten modelleert met TypeScript, is het heel gebruikelijk om een interface zoals deze te krijgen:

TypeScript

 

Het Probleem

De types van de eigenschappen hebben geen semantische betekenis. In termen van types zijn User.id, Order.id, Order.year, enz. hetzelfde: een nummer, en als nummer zijn ze uitwisselbaar, maar semantisch zijn ze dat niet.

Volgend op het vorige voorbeeld kunnen we een set functies hebben die acties over de entiteiten uitvoeren. Bijvoorbeeld:

TypeScript

 

Die functies zullen elk nummer in elke arg accepteren, ongeacht de semantische betekenis van het nummer. Bijvoorbeeld:

TypeScript

 

Het is duidelijk dat dit een grote fout is, en het lijkt misschien eenvoudig om te vermijden door de code te lezen, maar de code is niet altijd zo eenvoudig als het voorbeeld.

Hetzelfde gebeurt met getOrdersFiltered: we kunnen de waarden van dag en maand verwisselen, en we zullen geen waarschuwing of foutmelding krijgen. De fouten zullen zich voordoen als de dag groter is dan 12, maar het is duidelijk dat het resultaat niet zal zijn wat je verwacht.

De Oplossing

De regels van objectcalisthenics bieden een oplossing hiervoor: verpak alle primitieve types en Strings (gerelateerd aan de anti-patroon van primitieve obsessie). De regel is om de primitieve types in een object te verpakken dat een semantische betekenis vertegenwoordigt (DDD beschrijft dit als ValueObjects).

Maar met TypeScript hoeven we daarvoor geen klassen of objecten te gebruiken: we kunnen het typesysteem gebruiken om ervoor te zorgen dat een nummer dat iets anders dan een jaar vertegenwoordigt, niet kan worden gebruikt in plaats van een jaar.

Gemerkt Types

Dit patroon maakt gebruik van de uitbreidbaarheid van types om een eigenschap toe te voegen die de semantische betekenis waarborgt:

TypeScript

 

Deze eenvoudige regel maakt een nieuw type dat kan functioneren als een nummer — maar is geen nummer, het is een jaar.

TypeScript

 

Generalizing the Solution

Om te voorkomen dat we een type per merktype schrijven, kunnen we een hulptype maken zoals:

TypeScript

 

Dat een uniek symbool gebruikt als de merk-eigenschap naam om conflicten met uw eigenschappen te voorkomen en het oorspronkelijke type en het merk als generieke parameters krijgt.

Met dit kunnen we onze modellen en functies als volgt refactoren:

TypeScript

 

Nu zal de IDE in dit voorbeeld een fout tonen omdat id een UserId is en deleteOrder een OrderId verwacht.

TypeScript

 

Trade-Offs

Als een kleine trade-off moet u X gebruiken als Brand. Bijvoorbeeld, const year = 2012 as Year wanneer u een nieuwe waarde van een primitief maakt, maar dit is gelijk aan een new Year(2012) als u waarde-objecten gebruikt. U kunt een functie bieden die werkt als een soort “constructor”:

TypeScript

 

Validatie Met Merktype

Merktype zijn ook nuttig om ervoor te zorgen dat de gegevens geldig zijn, aangezien u specifieke types voor gevalideerde gegevens kunt hebben, en u kunt erop vertrouwen dat de gebruiker gevalideerd was door alleen types te gebruiken:

TypeScript

 

Readonly is niet verplicht, maar om er zeker van te zijn dat uw code de gegevens niet wijzigt na validatie, wordt het sterk aanbevolen.

Recap

Merktype zijn een eenvoudige oplossing die het volgende omvat:

  • Verbetert de leesbaarheid van de code: Maakt duidelijker welke waarde in elk argument moet worden gebruikt.
  • Betrouwbaarheid: Helpt om fouten in de code te vermijden die moeilijk te detecteren zijn; nu helpt de IDE (en de typecontrole) ons om te detecteren of de waarde op de juiste plaats staat
  • Gegevensvalidatie: Je kunt branded types gebruiken om ervoor te zorgen dat de gegevens geldig zijn.

Je kunt branded types zien als een soort versie van ValueObjects maar zonder gebruik te maken van klassen — alleen types en functies.

Geniet van de kracht van typings!

Source:
https://dzone.com/articles/branded-types-in-typescript