
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…


Vuoi avviare una nuova carriera o fare un upgrade?
Trova il corso Digital & Tech più adatto a te nel nostro catalogo!
- Tipi primitivi in Typescript
- Tipi letterali in Typescript
- Null e undefined in Typescript
- Tipi enumerativi in Typescript
- Oggetti in Typescript
- Array e tuple in Typescript
- Funzioni in Typescript
- Guardie di tipo in Typescript
- Any vs unknown in Typescript
- Tipi algebrici in Typescript
- Parametri di tipo in Typescript
- Manipolazione di tipi in Typescript
- Tipi utility 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 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.
CONTENUTI GRATUITI IN EVIDENZA
Guide per aspiranti programmatori 👨🏻🚀
Vuoi muovere i primi passi nel Digital e Tech? Abbiamo preparato alcune guide per aiutarti a orientarti negli ambiti più richiesti oggi.