Tipi di Marchio in TypeScript

Quando si modellano le entità con TypeScript, è molto comune ottenere un’interfaccia del genere:

TypeScript

 

Il Problema

I tipi delle proprietà non hanno alcun significato semantico. In termini di tipi, User.id, Order.id, Order.year, ecc. sono uguali: un numero, e come numero sono interscambiabili, ma semanticamente, non lo sono.

Seguendo l’esempio precedente, possiamo avere un insieme di funzioni che eseguono azioni sulle entità. Per esempio:

TypeScript

 

Queste funzioni accetteranno qualsiasi numero in qualsiasi argomento, indipendentemente dal significato semantico del numero. Per esempio:

TypeScript

 

Ovviamente, questo è un grande errore, e potrebbe sembrare facile evitarlo leggendo il codice, ma il codice non è sempre semplice come l’esempio.

Lo stesso accade con getOrdersFiltered: possiamo scambiare i valori di giorno e mese, e non otterremo alcun avviso o errore. Gli errori si verificheranno se il giorno è maggiore di 12, ma è ovvio che il risultato non sarà quello atteso.

La Soluzione

Le regole delle calisteniche degli oggetti forniscono una soluzione per questo: incapsulare tutti i primitivi e le stringhe (relativo all’anti-pattern dell’ossessione per i primitivi). La regola è di incapsulare i primitivi in un oggetto che rappresenta un significato semantico (DDD descrive ciò come ValueObjects).

Ma con TypeScript, non è necessario utilizzare classi o oggetti per questo: possiamo utilizzare il sistema di tipi per garantire che un numero che rappresenta qualcosa di diverso da un anno non possa essere utilizzato al posto di un anno.

Tipi Brandizzati

Questo modello utilizza l’estensibilità dei tipi per aggiungere una proprietà che garantisce il significato semantico:

TypeScript

 

Questa semplice riga crea un nuovo tipo che può funzionare come un numero — ma non è un numero, è un anno.

TypeScript

 

Generalizzando la soluzione

Per evitare di scrivere un tipo per ogni tipo di marchio, possiamo creare un tipo di utilità come:

TypeScript

 

Che utilizza un simbolo unico come nome della proprietà del marchio per evitare conflitti con le tue proprietà e ottiene il tipo originale e il marchio come parametri generici.

Con questo, possiamo rifattorizzare i nostri modelli e funzioni come segue:

TypeScript

 

Ora, in questo esempio, l’IDE mostrerà un errore poiché id è un UserId e deleteOrder si aspetta un OrderId.

TypeScript

 

Compromessi

Come piccolo compromesso, dovrai usare X come Brand. Ad esempio, const year = 2012 as Year quando crei un nuovo valore da un primitivo, ma questo è l’equivalente di un new Year(2012) se usi oggetti valore. Puoi fornire una funzione che funge da una sorta di “costruttore”:

TypeScript

 

Validazione con tipi di marchio

I tipi di marchio sono anche utili per garantire che i dati siano validi poiché puoi avere tipi specifici per dati validati, e puoi fidarti che l’utente sia stato convalidato semplicemente utilizzando i tipi:

TypeScript

 

Readonly non è obbligatorio, ma per essere sicuro che il tuo codice non modifichi i dati dopo averli convalidati, è molto raccomandato.

Riepilogo

I tipi di marchio sono una soluzione semplice che include quanto segue:

  • Migliora la leggibilità del codice: Rende più chiaro quale valore dovrebbe essere utilizzato in ciascun argomento
  • Affidabilità: Aiuta a evitare errori nel codice che possono essere difficili da rilevare; ora l’IDE (e il controllo dei tipi) ci aiutano a rilevare se il valore è nel posto corretto
  • Validazione dei dati: Puoi utilizzare i tipi marchiati per garantire che i dati siano validi.

Puoi pensare ai tipi marchiati come a una sorta di versione di ValueObjects ma senza utilizzare classi — solo tipi e funzioni.

Goditi il potere dei tipi!

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