CONTATTACI

Guide per aspiranti programmatori

sviluppatore che scrive al pc circondato da un vortice di foglietti
Lezione 23 / 30

Tipi algebrici in Typescript

Nelle sezioni iniziali di questa guida abbiamo parlato del fatto che TypeScript offre un type system statico, e questo comporta che una variabile non possa cambiare tipo durante il suo ciclo di vita. Eppure, sappiamo che il nostro codice TypeScript è destinato a diventare JavaScript, che invece ha un type system dinamico. Come abbiamo visto per gli array, un compromesso tra il rigore di TypeScript e la flessibilità di JavaScript consiste nell’indicare una combinazione dei possibili tipi che un dato potrebbe assumere: 

 

type Item = number | string | null; 
const items: Item[] = [null, 1, "hello", 42];

È finalmente giunto momento di sviscerare il principio che sta alla base di tipi come Item e dell’operatore |, ma anche del suo fratello &. Il motivo per cui affrontiamo questo argomento solo ora è che, un po’ come l’algebra, i tipi algebrici sono un costrutto tanto utile quanto avanzato; i tipi algebrici sono, generalmente, utilizzati in linguaggi funzionali come Haskell e F# che, per quanto stiano guadagnando popolarità, sono pur sempre dei linguaggi di programmazione meno diffusi dei linguaggi C-like come il linguaggio Java, C# e, per l’appunto, il linguaggio Javascript

In TypeScript è posssibile definire un tipo come combinazione di due o più altri tipi, secondo le operazioni insiemistiche di intersezione o unione. I tipi così definiti sono detti tipi algebrici (in inglese: algebraic data types). 

 

Tipo unione in Typescript (union type)

Con l’operatore | rappresentiamo un tipo unione (in inglese: union type), cioè un tipo che rappresenta la possibilità di appartenere a uno dei tipi elencati: 

 

type NumberOrString = number | string; 
type Bit = 0 | 1; 
type Bool = true | false; // Bool = boolean
let a: NumberOrString = 2; 
a = "hello"; 
a = new Date(); // Type 'Date' is not assignable to type 'NumberOrString'. 
let bit: Bit = 0; 
bit = 1; 
bit = 3; // Type '3' is not assignable to type 'Bit'. 
let bool: Bool = false; 
bool = true;

Con l’unione di tipi possiamo sfruttare la flessibilità di JavaScript mantenendo un certo rigore sulla definizione dei possibili tipi che la nostra variabile può assumere: quando andremo in lettura su una variabile che ha come tipo uno union type, TypeScript ci obbligherà a fare un narrowing o un’asserzione esplicita per identificare il sottotipo concreto che la variabile assume in quel punto del codice. 

Dal tipo Bool definito nell’esempio possiamo notare anche che TypeScript sia perfettamente consapevole che la sua definizione coincide con il tipo primitivo boolean, che, infatti, non è altro che un tipo che può assumere uno di due valori letterali. Un’altra categoria di tipi che possono solo assumere un elenco finito di valori letterali sono gli enum, che abbiamo visto in precedenza; infatti, è molto comune usare | con numeri o stringhe per creare una versione semplificata e leggera di enum

Vediamolo in un esempio con tanto di switch/case

 

// possiamo dichiarare un'unione anche su più righe 
type Status =  
 | "idle" 
 | "loading" 
 | "error" 
 | "success"; 
function logStatusMessage(status: Status): void { 
 switch (status) { 
 case "idle": 
 console.log("nothing happened :|"); 
 return; 
 case "loading": 
 console.log("please wait..."); 
 return; 
 case "error": 
 console.log("ops, something went wrong :("); 
 return; 
 case "success": 
 console.log("we did it! :D"); 
 return; 
 default: 
 // questo caso è teoricamente impossibile 
 throw new Error("Unreachable code"); 
 } 
}

Qui abbiamo fatto la stessa identica cosa che avremmo fatto con un enum, ma a tutti gli effetti ci siamo risparmiati l’implementazione dell’enum a runtime e abbiamo usato delle semplici stringhe come tag per i possibili casi. Nelle sezioni finali di questa guida ci spingeremo ancora oltre, associando per ogni caso un payload specifico, una tecnica molto comune per la gestione degli stati (in inglese: state management). 

 

Tipi intersezione in Typescript (Intersection Types)

Vediamo, ora, la controparte degli union types, cioè i tipi intersezione; anche in questo caso andiamo a combinare più tipi per formarne uno nuovo, ma questa volta il tipo risultante andrà a identificare i dati che appartengono a entrambi i tipi di partenza

 

type Entity = { id: number }; 
type User = { 
 name: string; 
 email: string; 
}; 
type UserEntity = Entity & User; 
const user: User = { 
 name: "John Doe", 
 email: "john.doe@example.com" 
}; 
const userEntity: UserEntity = { 
 id: 1, 
 ...user 
};

In questo caso, poiché userEntity ha tutte le proprietà richieste da User e da Entity, soddisfa il tipo UserEntity

Notiamo che, a differenza degli esempi fatti per gli union types, qui non abbiamo usato primitivi ma oggetti, e il motivo è presto rivelato: l’intersezione tra tipi individua quei dati che hanno l’unione delle proprietà di tali tipi; è controintuitivo, ma ha perfettamente senso da una prospettiva insiemistica, ed è altrettanto vero l’opposto per i tipi unione

Tuttavia, non essendoci proprietà compatibili per essere unite tra i tipi primitivi, l’intersezione tra questi è sempre il tipo never. Vediamolo nell’esempio, nel paragrafo a seguire.

 

Tipo never in Typescript

 

// A = B = C = never 
type A = number & string; 
type B = string & boolean; 
type C = number & boolean;

Queste intersezioni non sono molto utili dato che nessun valore (nemmeno null o undefined) può essere assegnato a never. Intuitivamente, risulta chiaro come un oggetto possa soddisfare più tipi se include tutte le proprietà richieste da ognuno di essi, ma in nessun caso una variabile potrà mai (per l’appunto, never) essere contemporaneamente un numero e una stringa, nemmeno nell’iperflessibile type system di JavaScript

Ovviamente la stessa cosa avviene per intersezioni di literal types

 

// A = B = C = never 
type A = "hello" & "hi"; 
type B = 1 & 4; 
type C = true & 42;

A questo punto, si potrebbe pensare che questo valga anche per l’intersezione tra primitivi e oggetti, ma c’è il tranello, che vediamo nel paragrafo successivo.

 

Branded types in Typescript 

 

type A = string & Date; 
type B = number & string[]; 
type C = 3 & { name: string };

Notiamo che nessuno di questi tipi viene ridotto a never, nonostante sia chiaramente impossibile creare variabili di questi tipi. Il motivo per questa strana eccezione è che ci consente di creare dei tipi speciali che rappresentano un payload di tipo primitivo in una struttura unica riconoscibile dal type checker, senza però incapsulare realmente questo in un oggetto. Questi tipi speciali vengono chiamati branded types (tipi marchiati) e abilitano delle particolari tecniche che ovviamente vedremo più avanti, non prima di aver finito il nostro tour del type system

Contattaci senza impegno per informazioni sul corso

Pagamento rateale

Valore della rata: A PARTIRE DA 115 €/mese.

Esempio di finanziamento 

Importo finanziato: € 2440 in 24 rate da € 115 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 2841.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

*In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Pagamento rateale

Valore della rata: A PARTIRE DA 187 €/mese.

Esempio di finanziamento  

Importo finanziato: € 3990 in 24 rate da € 187 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 4572.88.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Contattaci senza impegno per informazioni sul corso

Scopriamo insieme se i nostri corsi fanno per te. Compila il form e aspetta la chiamata di uno dei nostri consulenti.