Type Assertions e Type Casting in TypeScript

In TypeScript il compilatore cerca di determinare automaticamente il tipo dei valori e di verificare che vengano utilizzati correttamente. In alcuni casi, però, il programmatore possiede informazioni che il compilatore non può conoscere.

Per esempio, potremmo sapere che un valore ottenuto dalla pagina HTML è un determinato elemento, mentre TypeScript conosce soltanto un tipo più generico.

In situazioni di questo tipo possiamo utilizzare una Type Assertion, cioè indicare a TypeScript quale tipo considerare per un determinato valore.

La keyword as

La Type Assertion (asserzione di tipo) è un meccanismo con cui lo sviluppatore comunica esplicitamente al compilatore TypeScript di considerare un valore come un tipo specifico, evitando la type inference.

La sintassi più comune per una Type Assertion utilizza la keyword as:

const valore: unknown = "Ciao";

const testo = valore as string;

In questo caso valore è inizialmente di tipo unknown (keyword che abbiamo visto nella lezione sui valori sconosciuti in TypeScript). Con valore as string indichiamo al compilatore che vogliamo trattare quel valore come una string.

Possiamo quindi utilizzare proprietà e metodi disponibili per le stringhe:

console.log(testo.length);

La Type Assertion non esegue però una conversione. Ad esempio:

const valore: unknown = 100;

const testo = valore as string;

Nell'esempio, testo viene considerato da TypeScript come una string, ma il valore rimane effettivamente il numero 100 durante l'esecuzione.

Una Type Assertion, quindi, non equivale a una conversione come String() o Number().

Quando utilizzare as

Le Type Assertions sono utili soprattutto quando TypeScript dispone di informazioni insufficienti, ma noi conosciamo la struttura effettiva del valore.

Un esempio frequente riguarda il DOM (ovvero la pagina HTML, che puoi approfondire nella lezione sul DOM di HTML e JavaScript). Supponiamo di avere:

const input = document.querySelector("#nome");

Il compilatore non può sapere con certezza quale elemento HTML sia presente con quell'ID. Il risultato viene quindi considerato un tipo più generico.

Se sappiamo che l'elemento è un <input>, possiamo specificarlo:

const input = document.querySelector("#nome") as HTMLInputElement;

Ora TypeScript sa che input di un form è un HTMLInputElement e possiamo accedere alle sue proprietà specifiche:

console.log(input.value);

La Type Assertion è corretta soltanto se l'elemento recuperato è realmente compatibile con HTMLInputElement.

Sintassi alternativa per le Type Assertions

Esiste anche una seconda sintassi per le Type Assertions: const valore = <tipo>valore;.

Ad esempio:

const valore: unknown = "Ciao";

const testo = <string>valore;

Il risultato è equivalente alla sintassi:

const testo = valore as string;

La sintassi con as è però generalmente preferita nei progetti moderni.

L'operatore ! per i valori non nulli

TypeScript permette di utilizzare anche l'operatore !, chiamato in inglese Non-null Assertion Operator. L'operatore ! indica esplicitamente al compilatore di escludere i tipi null e undefined dal tipo statico della variabile.

Ad esempio:

const elemento = document.querySelector("#titolo");

Il risultato può essere null se l'elemento non esiste.

Se sappiamo con certezza che l'elemento è presente, possiamo usare:

const elemento = document.querySelector("#titolo")!;

Il ! dice a TypeScript di considerare il risultato come non nullo.

Possiamo quindi utilizzarlo senza effettuare un controllo esplicito:

elemento.textContent = "Ciao";

Type Assertions troppo permissive

Le Type Assertions devono essere utilizzate con attenzione perché permettono di ignorare parte dei controlli effettuati dal compilatore.

Ad esempio:

const valore: unknown = 123;

const testo = valore as string;

console.log(testo.toUpperCase());

TypeScript considera testo una stringa e quindi non segnala errori. Durante l'esecuzione, però, il valore è ancora il numero 123

L'utilizzo di testo.toUpperCase(); genererà comunque un errore.


Se abbiamo un valore unknown, spesso non è necessario utilizzare una Type Assertion. Possiamo controllarne il tipo:

const valore: unknown = "Ciao";

if (typeof valore === "string") {
    console.log(valore.toUpperCase());
}

In questo caso TypeScript riconosce automaticamente che, all'interno dell'if, valore è una string.

Questo approccio è generalmente più sicuro perché il controllo viene effettuato anche durante l'esecuzione.

Prova!Completa gli spazi vuoti con il testo appropriato.
// Specifica di trattare "citta" come stringa

const dato: unknown = "Milano";
const citta = dato string;
console.log(citta.length);