Conversioni, asserzioni e restringimento in Typescript | Aulab

GUIDE PER ASPIRANTI PROGRAMMATORI

Conversioni, asserzioni e restringimento in Typescript

Conversione di tipo in Typescript In diversi linguaggi di programmazione, il concetto di tipo si accompagna al concetto di type casting, cioè di conversione di tipo. Senza scomodare altri linguaggi, vediamo il seguente codice JavaScript: const one = Number("1"); Abbiamo il valore letterale “1” che rappresenta una stringa, convertito in numero dalla funzione Number e…

Lezione 12 / 30
Enza Neri
Immagine di copertina

Vuoi avviare una nuova carriera o fare un upgrade?

Trova il corso Digital & Tech più adatto a te nel nostro catalogo!

Conversione di tipo in Typescript

In diversi linguaggi di programmazione, il concetto di tipo si accompagna al concetto di type casting, cioè di conversione di tipo. Senza scomodare altri linguaggi, vediamo il seguente codice JavaScript:

const one = Number("1");

Abbiamo il valore letterale “1” che rappresenta una stringa, convertito in numero dalla funzione Number e conservato nella costante one. In altre parole, abbiamo effettuato una conversione di tipo. Se tale conversione non fosse stata possibile, Number avrebbe restituito NaN (not a number, che ironicamente è un valore di tipo number). Lo stesso tipo di conversione in JavaScript può essere effettuato analogamente con Boolean e String

In questo modo possiamo effettuare la conversione tra tipi semplici in JavaScript, e TypeScript sarà sempre allineato sui tipi in uso. 

Immaginiamo ora di avere un’informazione di tipo che TypeScript non può in alcun modo inferire: 

const data = JSON.parse('{ "name": "John" }'); // const data: any 
console.log(data.name);

In questo caso, l’argomento di JSON.parse è semplicemente una stringa, e potenzialmente questa stringa potrebbe venire da un punto esterno al nostro codice, come un file, un database o una risposta HTTP; quindi, TypeScript non può dedurre che data ha una proprietà name di tipo string! 

Come vedremo più avanti, any è un tipo speciale che rappresenta qualunque tipo, ed è usato appositamente per sopprimere gli avvertimenti del type checker sull’uso di una certa variabile, motivo per cui qui TypeScript non batte ciglio alla vista di data.name. Vedremo anche come è possibile rendere TypeScript meno “ingenuo” di fronte ai valori di tipo sconosciuto

Quindi, come facciamo a tipizzare data?  Vediamolo.

Asserzione di tipo in Typescript

Ricollegandoci all’esempio precedente, per tipizzare data ci viene incontro la keyword as, che introduce un’asserzione di tipo (in inglese: type assertion). Usando as stiamo dicendo al type checker: “quest’informazione te la do io, e ti puoi fidare”. 

Vediamolo in pratica: 

const data = JSON.parse('{ "name": "John" }') as { name: string };

Ora che abbiamo istruito TypeScript su come qualificare data, se digitiamo data. nell’IDE, ci verrà automaticamente suggerito di navigare verso la proprietà name

Vediamo ora il seguente esempio: 

const a = 1; // const a: number 
const b = a as string; // Conversion of type 'number' to type 'string' may be a mistake ...

In questo caso, TypeScript ci sta segnalando che la nostra type assertion è priva di senso e probabilmente errata, perché in base alle informazioni disponibili sul tipo di a, non è possibile asserire che b sia una stringa. Dunque TypeScript ammette asserzioni solo laddove si vada ad allargare oppure a restringere la specificità di un tipo. 

In pratica, qui il problema è che il tipo string non è un sottoinsieme del tipo number né viceversa, quindi non ha senso interpretare un numero come una stringa; avrebbe invece senso convertire un numero in una stringa: 

const a = 1; // const a: number 
const b = String(a); // const b: string

Prendiamo, invece, la seguente funzione: 

function concat(a: string | number, b: string | number): string { 
 return a + b; // Operator '+' cannot be applied to types 'string | number' and 'string | number'. 
}

Qui TypeScript, giustamente, protesta perché l’operatore + ha due comportamenti diversi se applicato a numeri o a stringhe. In questo caso, noi potremmo asserire che a e b siano stringhe, ma finiremmo con l’introdurre un bug, perché se questi fossero numeri, il risultato della funzione non sarebbe la loro concatenazione, ma la loro somma algebrica. 

Dunque, la cosa migliore sarebbe operare una conversione a stringa, e ci basterà farlo su a perché è sufficiente che uno dei due operandi sia stringa per determinare il comportamento dell’operatore +

function concat(a: string | number, b: string | number): string { 
 return String(a) + b; 
}

A questo punto, per TypeScript è tutto a posto: un numero può sempre essere convertito in una stringa, e l’operatore + applicato dopo una stringa eseguirà in automatico un’eventuale conversione a stringa anche per b, restituendo la concatenazione anziché la somma algebrica. 

Ma immaginiamo di voler effettuare la conversione solo se necessario; significa che, nel caso in cui a fosse già una stringa, potremmo procedere direttamente alla “somma” tra stringhe: 

function concat(a: string | number, b: string | number): string { 
 if (typeof a === "string") return a + b; // a: string 
 return String(a) + b; 
}

In questo caso TypeScript non protesterà, perché il type checker è in grado di intuire che a è una stringa nel primo caso, e un numero nel secondo. 

La capacità di TypeScript di restringere il tipo di una variabile tra più tipi possibili in base alle condizioni verificate dal codice è detta restringimento di tipo, in inglese type narrowing

Restringimento di tipo in Typescript

Esistono molti modi in cui TypeScript può restringere il tipo di una variabile, vediamone alcuni: 

function f(a: number | string | number[] | null) { 
 // valorizzazione/non-nullità 
 if (a) ... // a: number | string | number[] 
 // typeof 
 if (typeof a === "number") ... // a: number 
 if (typeof a === "object") ... // a: number[] | null 
 // instanceof 
 if (a instanceof Array) ... // a: number[] 
 // uguaglianza 
 if (a === null) ... // a: null 
}

In pratica, invece di prenderci noi la responsabilità di fare un’asserzione, faremo delle verifiche di tipo a runtime, come se stessimo lavorando in JavaScript puro: TypeScript a quel punto saprà con assoluta certezza che i tipi possibili di quella variabile saranno ristretti in base alla condizione verificata. 

Abbiamo dunque visto come muoverci tra diversi tipi, ma abbiamo appena grattato la superficie; prima di addentrarci nel dettaglio del type system, vediamo come TypeScript ci aiuta non solo a validare il codice, ma anche e soprattutto a scriverlo

Sei indeciso sul percorso? 💭

Parliamone! Scrivici su Whatsapp e risponderemo a tutte le tue domande per capire quale dei nostri corsi è il più adatto alle tue esigenze.

Oppure chiamaci al 800 128 626