Hai un progetto JavaScript che cresce e gli errori 'undefined is not a function' a runtime diventano il tuo incubo quotidiano? Passi più tempo a fare debug che a scrivere feature nuove? Noi di Meteora Web ci siamo passati tante volte. Quando gestiamo piattaforme per i nostri clienti – e-commerce con WooCommerce custom, CRM in Laravel con frontend Vue, app React – sapere che un tipo sbagliato può mandare in crash tutto il sistema non è un'opzione. TypeScript non è una moda: è la risposta concreta a un problema economico. Ogni bug che intercetti a compile time è ora di sviluppo che non paghi in assistenza post-lancio. Ogni funzione tipizzata è documentazione viva che il tuo team futuro ti ringrazierà.
In questa pillar page vediamo TypeScript da zero ad avanzato: dalla configurazione del tsconfig.json ai Generics, dagli Utility Types all'integrazione con React e Node.js. Nessun manuale accademico, ma quello che usiamo ogni giorno in produzione. Pronti?
Perché TypeScript è meglio di JavaScript puro per progetti reali?
JavaScript è un linguaggio fantastico per iniziare, ma quando il codice supera le 10.000 righe – o quando lavori in team – l'assenza di tipi si trasforma in costo. Un parametro passato come stringa invece che numero viene scoperto solo a runtime, magari in produzione. Noi, in passato, abbiamo visto interi carrelli e-commerce bloccarsi per un typeof dimenticato. Con TypeScript il compilatore (o l'IDE) ti avvisa prima di eseguire il codice.
Vantaggi misurabili:
- Riduzione dei bug del 15-20% secondo studi accademici (vedi Typescript vs JavaScript: A Study on Code Quality).
- Refactoring sicuro: cambi un tipo e il compilatore segnala tutte le violazioni.
- Autocompletamento e documentazione inline: il tuo editor diventa un'estensione del cervello.
- Meno test unitari necessari: il type-checking copre già molti casi limite.
Azione concreta: Se stai iniziando un nuovo progetto, parti direttamente con TypeScript. Se hai un progetto JS esistente, attiva allowJs nel tsconfig e inizia a rinominare file da .js a .ts partendo dai moduli più critici.
Sponsored Protocol
Come configurare TypeScript correttamente con tsconfig.json?
Il file tsconfig.json è il cuore della configurazione. Noi usiamo quasi sempre queste opzioni come base:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}Spiegazione:
strict: true– abilita tutte le verifiche rigorose (noImplicitAny, strictNullChecks, etc.). È il vero valore aggiunto di TypeScript.target: ES2020– permette async/await, Promise, Map, Set senza polyfill.module– per Node.js usiamo commonjs; per frontend moderno o con bundler usiamo ES2020 o ESNext.esModuleInterop– evita problemi con import/export e moduli CommonJS.skipLibCheck– velocizza la compilazione saltando i controlli dei file .d.ts esterni.
Azione concreta: Crea un nuovo progetto con npx tsc --init e poi modificalo con le opzioni sopra. Esegui npm install typescript --save-dev e avvia la compilazione con npx tsc.
Quali sono i tipi base di TypeScript e come usarli?
TypeScript estende i tipi di JavaScript con alcuni fondamentali:
any– disabilita il type-checking. Da evitare quasi sempre.unknown– come any, ma obbliga a un controllo prima di usare il valore.never– rappresenta un valore che non si verifica mai (es. funzione che lancia sempre eccezione).void– per funzioni che non restituiscono nulla.nulleundefined– con strictNullChecks attivo non sono assegnabili automaticamente.
let nome: string = "Mario";
let eta: number = 30;
let isAdmin: boolean = false;
// unknown richiede type guard
let input: unknown = "ciao";
if (typeof input === "string") {
console.log(input.toUpperCase());
}
// never
function errore(messaggio: string): never {
throw new Error(messaggio);
}Azione concreta: Rivedi il tuo codice: trasforma ogni any in unknown e aggiungi type guard dove necessario. Attiva noImplicitAny per catturare variabili non tipizzate.
Sponsored Protocol
Interface vs Type: quando usare quale in TypeScript?
Sia interface che type permettono di definire strutture dati, ma hanno differenze pratiche:
- Interface – può essere estesa (extends), dichiarata più volte (declaration merging). Ideale per oggetti e classi.
- Type – può creare union, intersection, mapped types. Più flessibile per composizioni complesse.
interface Utente {
nome: string;
eta?: number;
}
interface Admin extends Utente {
ruolo: "admin";
}
type ID = string | number;
type Ruolo = "user" | "admin";
type UtenteCompleto = Utente & { ruolo: Ruolo };Regola pratica: Usa interface per oggetti che rappresentano entità (User, Product) e type per alias di tipi primitivi, union o tuple. Noi, nei nostri progetti, preferiamo interface per API e type per utility.
Azione concreta: Scegli una parte del tuo codice e converti le definizioni di oggetto da type a interface, verificando se hai bisogno di declaration merging.
Come funzionano i Generics in TypeScript per codice riutilizzabile?
I Generics permettono di scrivere funzioni, classi e interfacce che lavorano con tipi senza conoscerli a priori. È come avere un placeholder di tipo che viene legato al momento dell'uso.
function primoElemento<T>(array: T[]): T | undefined {
return array[0];
}
const primoNumero = primoElemento([1, 2, 3]); // number
const primaStringa = primoElemento(["a", "b"]); // stringVincoli (constraints) con extends per limitare i tipi ammessi:
function ottieniLunghezza<T extends { length: number }>(item: T): number {
return item.length;
}
ottieniLunghezza("ciao"); // ok, string ha length
ottieniLunghezza([1,2]); // okApplicazione pratica: I Generics sono alla base di librerie come React (type dei props), Express (Request/Response tipizzati) e utility type come Pick, Omit. Noi li usiamo quotidianamente per creare hook generici o middleware tipizzati.
Azione concreta: Scrivi una funzione generica che accetti un array e restituisca un oggetto con il primo e ultimo elemento. Tipizza anche l'oggetto di ritorno.
Sponsored Protocol
Quali Utility Types di TypeScript ti servono davvero?
TypeScript mette a disposizione una serie di utility type che trasformano i tipi esistenti. Ecco i più usati:
Partial<T>– rende tutte le proprietà opzionali.Required<T>– rende tutte le proprietà obbligatorie.Readonly<T>– rende le proprietà di sola lettura.Pick<T, K>– estrae un sottoinsieme di proprietà.Omit<T, K>– esclude un insieme di proprietà.Record<K, T>– crea un oggetto con chiavi di tipo K e valori di tipo T.
interface Prodotto {
id: number;
nome: string;
prezzo: number;
}
// Per aggiornamenti parziali
const aggiornamento: Partial<Prodotto> = { prezzo: 29.99 };
// Per un report senza id
const report: Omit<Prodotto, "id"> = { nome: "Scarpe", prezzo: 99 };
// Per mappare utenti per ID
const mappa: Record<number, Utente> = { 1: { nome: "Mario" } };Azione concreta: Rivedi le tue interfacce e usa Partial per funzioni di update, Pick per viste parziali, Readonly per oggetti che non devono essere modificati.
Come usare TypeScript con React per componenti type-safe?
Con React, TypeScript migliora radicalmente l'esperienza di sviluppo. Ecco gli aspetti essenziali:
Props tipizzate
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: "primary" | "secondary";
}
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled, variant }) => {
return <button onClick={onClick} disabled={disabled} className={variant}>{label}</button>;
};Hook generici
function useForm<T>(initial: T) {
const [values, setValues] = useState<T>(initial);
const handleChange = (key: keyof T, value: T[keyof T]) => {
setValues(prev => ({ ...prev, [key]: value }));
};
return { values, handleChange };
}TypeScript ti permette anche tipizzare eventi e ref in modo preciso:
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};Attenzione: usa React.FC con cautela perché aggiunge automaticamente i figli. Preferisci interfacce esplicite per i children se necessario.
Sponsored Protocol
Azione concreta: Se lavori con React, aggiungi TypeScript al tuo progetto con npm install @types/react @types/react-dom e inizia a tipizzare i componenti uno alla volta.
Come integrare TypeScript con Node.js e Express?
Per backend tipizzato, TypeScript è un alleato potente. Con Express, però, la tipizzazione di request e response non è automatica.
import express, { Request, Response, NextFunction } from 'express';
interface Utente {
id: number;
nome: string;
}
const app = express();
app.get('/api/users/:id', (req: Request, res: Response) => {
const userId = parseInt(req.params.id);
// ... logica
const utente: Utente = { id: userId, nome: 'Mario' };
res.json(utente);
});
// Middleware tipizzato
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).send('Errore!');
});Per gestire meglio le richieste con body, puoi creare interfacce personalizzate e persino estendere l'oggetto Request di Express con moduli augment (declaration merging).
Azione concreta: In un progetto Node.js esistente, installa @types/express e converti il primo endpoint in TypeScript. Aggiungi l'opzione strict per vedere dove mancano tipi.
I Decorators in TypeScript: servono davvero a qualcosa?
I decorators sono una feature sperimentale (attualmente in stage 2) che permette di aggiungere metadati a classi, metodi e proprietà. Framework come NestJS li usano per definire dependency injection, routing, validazione.
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Chiamato ${propertyKey} con`, args);
return original.apply(this, args);
};
}
class MiaClasse {
@log
saluta(nome: string) {
return `Ciao ${nome}`;
}
}Per abilitarli, devi impostare experimentalDecorators: true nel tsconfig.json. Noi li usiamo pochissimo al di fuori di NestJS e librerie di validazione come class-validator. Se non lavori con quei framework, probabilmente non ti servono. Meglio concentrarsi su pattern più moderni come composizione funzionale.
Sponsored Protocol
Azione concreta: Se usi NestJS o TypeORM, studia come i decoratori semplificano la dichiarazione dei modelli. Altrimenti, salta pure questa feature.
Come migrare un progetto da JavaScript a TypeScript senza dolore?
La migrazione può essere graduale. Strategia collaudata:
- Installa TypeScript e crea un
tsconfig.jsonconallowJs: trueestrict: falseinizialmente. - Rinomina i file da
.jsa.tspartendo da quelli con meno dipendenze. - Attiva
strict: truesolo dopo aver tipizzato la maggior parte dei moduli. - Usa
// @ts-expect-errortemporaneamente per bloccare errori noti che risolverai dopo. - Per i file che non vuoi ancora tipizzare, mantieni l'estensione
.jse importali normalmente.
Noi abbiamo migrato un progetto Laravel+Vue con 20.000 righe in circa 2 settimane lavorando per moduli, partendo dal backend API.
Azione concreta: Scegli un modulo isolato (es. una utility function) e convertilo in TypeScript con tipi di base. Misura quanti bug emergono grazie al type-checking.
In sintesi: cosa fare adesso
- Inizia subito: se non usi TypeScript, installalo nel tuo progetto attuale con
allowJse parti da un file. - Configura
strict: true: è la scelta che dà più valore. Affronta i warning uno a uno. - Usa i Generics per funzioni e componenti riutilizzabili – riducono codice duplicato.
- Utility Types per non reinventare la ruota: Partial, Pick, Omit ti salvano tempo.
- Non avere paura degli errori: ogni errore di compilazione è un bug che non arriva in produzione.
TypeScript non è un ornamento: è uno strumento che aumenta la qualità del codice e riduce i costi di manutenzione. Noi di Meteora Web lo usiamo ogni giorno – da stack Laravel/Vue a piattaforme React personalizzate – e i risultati parlano chiaro: meno bug, team più produttivi, clienti più soddisfatti. Se vuoi approfondire un aspetto specifico, scrivici o consulta la documentazione ufficiale e le cheatsheet. Buon coding.