TypeScript 5.x ha ridefinito le aspettative di chi sviluppa in JavaScript tipizzato. Non si tratta solo di patch o bugfix: ogni versione della serie 5 ha portato feature che cambiano concretamente come si scrive e si ragiona il codice. In questa guida esploriamo le novità più importanti, con esempi pratici e pattern reali da usare oggi.
Se stai ancora su TypeScript 4.x, questa è la tua roadmap per capire cosa ti stai perdendo — e come migrare senza dolore. Se sei già su TS 5, troverai dettagli e casi d’uso che probabilmente non hai ancora esplorato.
1. satisfies Operator: Inferenza Senza Perdita di Tipo
Introdotto in TypeScript 4.9 e diventato pattern fondamentale in 5.x, l’operatore satisfies risolve uno dei problemi più frustranti del type system: validare che un oggetto rispetti un tipo senza perdere l’inferenza specifica.
Il problema classico con annotazione esplicita:
// PRIMA — annotazione esplicita perde informazione
const palette: Record<string, string | string[]> = {
red: "#ff0000",
blue: ["#0000ff", "#0033cc"],
};
// palette.red è string | string[] — perdiamo il fatto che è solo string
palette.red.toUpperCase(); // ERRORE: toUpperCase non esiste su string[]
// CON satisfies — TS 5.x
const palette = {
red: "#ff0000",
blue: ["#0000ff", "#0033cc"],
} satisfies Record<string, string | string[]>;
// palette.red è string — inferenza preservata!
palette.red.toUpperCase(); // OK
palette.blue.map(c => c.toUpperCase()); // OKIl caso d’uso più potente è la validazione di configurazioni complesse:
type Config = {
db: { host: string; port: number };
cache: { ttl: number; maxSize: number };
features: Record<string, boolean>;
};
const config = {
db: { host: "localhost", port: 5432 },
cache: { ttl: 3600, maxSize: 1000 },
features: { darkMode: true, beta: false },
} satisfies Config;
// config.db.port è number (non string | number)
// config.features.darkMode è boolean💡 Tip: Usa
satisfiesogni volta che vuoi validare la forma di un oggetto mantenendo l’autocomplete specifico sui valori. È particolarmente utile per config files, route definitions e mapping objects.
2. const Type Parameters: Inferenza Letterale Controllata
TypeScript 5.0 ha introdotto i const type parameters, una feature che risolve il problema dell’inferenza troppo generica nelle funzioni generiche.
// PRIMA — inferenza troppo generica
function identity<T>(value: T): T { return value; }
const result = identity(["a", "b", "c"]);
// result: string[] — perdiamo la tupla letterale
// TS 5.0 — const type parameter
function identityConst<const T>(value: T): T { return value; }
const result2 = identityConst(["a", "b", "c"]);
// result2: readonly ["a", "b", "c"] — mantiene la tupla letterale!Utilissimo per builder pattern e DSL type-safe:
function createRoutes<const T extends Record<string, string>>(routes: T): T {
return routes;
}
const routes = createRoutes({
home: "/",
about: "/about",
blog: "/blog/:slug",
});
// routes.home: "/" — tipo letterale, non string generico
// TypeScript sa esattamente quali route esistono🔧 Pattern: Combina
consttype parameter consatisfiesper il massimo controllo sui builder pattern.const T extends Schemavalida e preserva simultaneamente.
3. Decorators Standard ECMAScript: Finalmente Stabili
TypeScript 5.0 ha implementato i decorators secondo lo standard ECMAScript Stage 3, abbandonando l’implementazione sperimentale che aveva caratterizzato TS per anni. Non sono la stessa cosa — la nuova API è più pulita e prevedibile.
// NUOVO standard ECMAScript decorators (TS 5.0+)
// NON serve più "experimentalDecorators": true in tsconfig
function logged(target: Function, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
return function (this: unknown, ...args: unknown[]) {
console.log(`[LOG] Calling ${methodName} with`, args);
const result = target.call(this, ...args);
console.log(`[LOG] ${methodName} returned`, result);
return result;
};
}
class UserService {
@logged
getUser(id: number) {
return { id, name: "Gioacchino" };
}
}
const service = new UserService();
service.getUser(42);
// [LOG] Calling getUser with [42]
// [LOG] getUser returned { id: 42, name: 'Gioacchino' }Il ClassMethodDecoratorContext offre accesso a metadata ricchi:
function memoize<T, Args extends unknown[], R>(
target: (this: T, ...args: Args) => R,
context: ClassMethodDecoratorContext<T, (this: T, ...args: Args) => R>
) {
const cache = new Map<string, R>();
return function (this: T, ...args: Args): R {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key)!;
const result = target.call(this, ...args);
cache.set(key, result);
return result;
};
}
class Calculator {
@memoize
fibonacci(n: number): number {
if (n <= 1) return n;
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}🎯 Migrazione: Se usi i vecchi experimental decorators con
emitDecoratorMetadata, attenzione: i nuovi decorators standard NON emettono metadata automaticamente. Valuta se la tua libreria (es. TypeORM, NestJS) supporta già il nuovo standard.
4. --verbatimModuleSyntax e infer extends: Precisione Avanzata
TypeScript 5.0 introduce --verbatimModuleSyntax, un flag che obbliga a essere espliciti sulle import di tipo. Niente più ambiguità tra import runtime e import type-only.
// Con --verbatimModuleSyntax attivo:
// ERRORE — TS non sa se MyType è usato a runtime
import { MyType } from "./types";
// CORRETTO — esplicitiamo che è solo un tipo
import type { MyType } from "./types";
// Oppure inline type import
import { myFunction, type MyType } from "./module";
// Il vantaggio: tree-shaking perfetto e zero runtime overheadinfer extends (TS 5.0) risolve il problema del narrowing nei conditional types:
// PRIMA — risultato troppo generico
type FirstElement<T extends unknown[]> =
T extends [infer F, ...unknown[]] ? F : never;
type Nums = FirstElement<[1, 2, 3]>; // number — non letterale!
// CON infer extends — TS 5.0+
type FirstElementLiteral<T extends readonly unknown[]> =
T extends [infer F extends number, ...unknown[]] ? F : never;
type Num = FirstElementLiteral<[1, 2, 3]>; // 1 — tipo letterale!
// Caso pratico: estrarre chiavi da union di stringhe
type ExtractRouteParams<T extends string> =
T extends `${string}:${infer Param extends string}/${infer Rest extends string}`
? Param | ExtractRouteParams<`/${Rest}`>
: T extends `${string}:${infer Param extends string}`
? Param
: never;
type Params = ExtractRouteParams<"/users/:id/posts/:postId">;
// "id" | "postId"5. Performance e Migrazione da TypeScript 4.x
TypeScript 5.x porta miglioramenti significativi alle performance del compilatore. Il team ha introdotto ottimizzazioni interne che riducono il tempo di type-checking fino al 10-15% su progetti grandi. Il refactor da namespace a moduli ES ha anche ridotto le dimensioni del bundle del compilatore del ~26%.
Per il progetto che vuole integrarsi con Next.js 16, la compatibilità con TypeScript 5.x è fondamentale — Turbopack usa nativamente le nuove API.
// tsconfig.json consigliato per TS 5.x
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16", // o "Bundler" per Vite/webpack
"moduleResolution": "Node16", // allineato al module
"verbatimModuleSyntax": true, // nuovo in 5.0
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}
// Checklist migrazione TS 4.x → 5.x:
// 1. Rimuovi "experimentalDecorators" se usi i nuovi decorators
// 2. Aggiungi "verbatimModuleSyntax" e correggi le import
// 3. Aggiorna @types/* packages
// 4. Controlla breaking changes in enum handling
// 5. Verifica compatibilità librerie (specie NestJS, TypeORM)Un’altra novità da non trascurare: il supporto migliorato per i resolution-mode nelle import assertions, che permette di specificare esplicitamente se una dipendenza deve essere risolta come CommonJS o ESM:
// Import con resolution-mode esplicito
import type { SomeType } from "some-lib" with { "resolution-mode": "require" };
import type { OtherType } from "other-lib" with { "resolution-mode": "import" };FAQ: TypeScript 5.x — Domande Frequenti
Devo riscrivere tutto per migrare a TS 5?
No. TypeScript 5.x è largamente retrocompatibile con 4.x. Le breaking change principali riguardano i decorators sperimentali e alcuni edge case con gli enum. La maggior parte dei progetti migra con zero o pochissime modifiche.
satisfies vs annotazione esplicita: quando usare quale?
Usa annotazione esplicita (const x: Type = ...) quando vuoi che il tipo sia esattamente Type senza dettagli aggiuntivi. Usa satisfies quando vuoi validare la forma ma mantenere l’inferenza specifica dei valori. Per le API pubbliche di librerie, l’annotazione esplicita è più sicura.
I nuovi decorators funzionano con Angular e NestJS?
Angular (dal v15+) ha iniziato la transizione ai decorators standard. NestJS è ancora prevalentemente sui decorators sperimentali per compatibilità con reflect-metadata. Controlla la documentazione della tua framework prima di rimuovere experimentalDecorators.
--verbatimModuleSyntax è obbligatorio?
No, è opt-in. Ma è fortemente consigliato nei nuovi progetti perché migliora la chiarezza del codice e ottimizza il tree-shaking. Su progetti esistenti, attivarlo richiede di correggere tutte le import ambigue — usa la modalità --verbatimModuleSyntax con IDE support per trovare automaticamente i file da aggiornare.
Dove trovo le release notes complete di TypeScript?
Le release notes ufficiali sono su typescriptlang.org/docs/handbook/release-notes. Per testare le feature interattivamente, usa il TypeScript Playground che supporta sempre l’ultima versione stabile e le beta.

