Perché 'as' in TypeScript è spesso pericoloso?

Usi TypeScript per la sicurezza che porta al tuo codice, ma avrai sicuramente notato che l'operatore as permette di forzare la compilazione anche quando TypeScript sospetta tipi incompatibili. Questa flessibilità può essere allettante, ma spesso nasconde delle trappole. Scopriamo insieme perché l'uso eccessivo di as può introdurre errori di runtime e come evitare queste insidie.

Il problema di as: errori nascosti durante la compilazione

In TypeScript, l'operatore as permette di fare quello che chiamiamo type assertion, ovvero dire a TypeScript "Fidati di me, so quello che sto facendo." Tuttavia, questo strumento può talvolta essere una cattiva idea, perché aggira i controlli dei tipi. Quando usi as, ti assumi la responsabilità di garantire che il tipo sia effettivamente corretto, anche se TypeScript non può verificarlo. Vediamo questo con alcuni esempi concreti.

Esempio 1: Forzare un tipo con as che non è realmente compatibile

Prendiamo un esempio dove hai un oggetto di tipo Person con alcune proprietà ben definite, ma decidi di usare as per forzarlo a un tipo diverso, pensando che funzionerà.

typescript
1type Person = {
2 name: string;
3 age: number;
4};
5
6type Employee = {
7 name: string;
8 age: number;
9 role: string;
10};
11
12const person: Person = { name: 'Alice', age: 30 };
13
14// Forza person in Employee, anche se 'role' non è definito
15const employee = person as Employee;
16
17console.log(employee.role); // Errore di runtime: 'role' è undefined

In questo esempio, employee non ha la proprietà role, ma TypeScript non restituisce un errore durante la compilazione grazie a as. Tuttavia, durante l'esecuzione, employee.role è undefined, il che può causare errori se il tuo codice si aspetta un valore di tipo string.

Esempio 2: Ignorare i controlli con as unknown as

A volte, vediamo gli sviluppatori utilizzare casting a catena per forzare un tipo tramite unknown, come in questo esempio:

typescript
1function processData(data: any) {
2 // Forza data in numero, anche se non lo è
3 const numberData = data as unknown as number;
4 console.log(numberData + 10);
5}
6
7processData('testo'); // Errore di runtime: NaN

Qui, prendiamo un valore data che potrebbe essere qualsiasi cosa e lo castiamo in number tramite unknown. Durante la compilazione, tutto sembra funzionare, ma durante l'esecuzione, l'addizione produce NaN perché data era in realtà una stringa.

Esempio 3: Manipolare oggetti parziali

Prendiamo un altro caso classico dove forziamo un oggetto parzialmente completato a un tipo completo pensando che andrà tutto bene:

typescript
1type Product = {
2 id: number;
3 name: string;
4 price: number;
5};
6
7const partialProduct = { id: 1, name: 'Book' } as Product;
8
9console.log(partialProduct.price); // Errore di runtime: 'price' è undefined

L'oggetto partialProduct viene convertito in Product anche se manca la proprietà price. Questa assenza non viene rilevata da TypeScript grazie a as, ma porta a un undefined che può generare errori di runtime se questo valore viene utilizzato senza una verifica preliminare.

Come evitare gli errori con Zod

Per evitare i problemi generati da as, una buona pratica è validare i dati durante l'esecuzione, in particolare se provengono da una fonte esterna. È qui che entra in gioco Zod. Zod è una libreria di validazione degli schemi in TypeScript che permette di definire tipi sicuri e validarli a runtime.

Con Zod, invece di forzare un tipo con as, puoi validare e convertire i dati con uno schema predefinito. Per esempio:

typescript
1import { z } from 'zod';
2
3// Definizione di uno schema per Product
4const ProductSchema = z.object({
5 id: z.number(),
6 name: z.string(),
7 price: z.number(),
8});
9
10// Validazione dei dati con Zod
11const result = ProductSchema.safeParse(partialProduct);
12
13if (result.success) {
14 const product = result.data;
15 console.log('Prodotto valido:', product);
16} else {
17 console.error('Errore di validazione:', result.error.errors);
18}

In questo esempio, Zod verifica che partialProduct rispetti effettivamente lo schema Product. Se mancano delle proprietà, Zod restituisce un errore di validazione invece di lasciare passare valori undefined. Grazie a Zod, metti in sicurezza i tuoi dati ed eviti errori di runtime legati a tipi incompleti o incorretti.

Conclusione

L'operatore as può essere una soluzione rapida per aggirare i controlli di TypeScript, ma introduce rischi di errori di runtime, soprattutto quando si forzano tipi senza validazione. Utilizzando una libreria come Zod, puoi validare i tuoi dati a runtime e così sfruttare pienamente la sicurezza di TypeScript, anche nei casi in cui as sembrava l'unica soluzione.

Condividi questo articolo


Sébastien Timoner

Sébastien TIMONER

Lead Developer
Esperto in Sviluppo su Misura
Aix-en-Provence, France

Esperto nello sviluppo web e nella gestione di team tecnici, mi specializzo nella creazione e ottimizzazione di soluzioni digitali performanti. Grazie a una profonda padronanza di tecnologie moderne come React.js, Node.js, TypeScript e Symfony, garantisco il successo di progetti SaaS complessi, dalla progettazione alla messa in produzione, per aziende di diversi settori, all'interno di offroadLabs.

In offroadLabs, offro servizi di sviluppo su misura, combinando competenza tecnica e approccio collaborativo. Che si tratti di creare una soluzione SaaS innovativa, modernizzare un'applicazione esistente o accompagnare la crescita professionale di un team, mi impegno a fornire soluzioni robuste e performanti, adattate alle esigenze specifiche di ogni progetto.

Sono disponibile per incarichi intorno ad Aix-en-Provence o in full remote.