Novità di TypeScript 5.0: dichiaratori, tipo di cost, miglioramento dell'enumerazione, velocità e molto altro!
Pubblicato: 2023-04-15TypeScript 5.0 è stato rilasciato ufficialmente il 16 marzo 2023 ed è ora disponibile per tutti. Questa versione introduce molte nuove funzionalità con l'obiettivo di rendere TypeScript più piccolo, più semplice e più veloce.
Questa nuova versione modernizza i decoratori per la personalizzazione delle classi, consentendo la personalizzazione delle classi e dei loro membri in modo riutilizzabile. Gli sviluppatori possono ora aggiungere un modificatore const a una dichiarazione di parametro di tipo, consentendo alle inferenze di tipo const di essere l'impostazione predefinita. La nuova versione rende anche tutte le enumerazioni unificate, semplificando la struttura del codice e velocizzando l'esperienza di TypeScript.
In questo articolo, esplorerai le modifiche introdotte in TypeScript 5.0, fornendo uno sguardo approfondito alle sue nuove funzionalità e capacità.
Introduzione a TypeScript 5.0
TypeScript è un compilatore ufficiale che puoi installare nel tuo progetto usando npm. Se vuoi iniziare a usare TypeScript 5.0 nel tuo progetto, puoi eseguire il seguente comando nella directory del tuo progetto:
npm install -D typescript
Questo installerà il compilatore nella directory node_modules , che ora puoi eseguire con il comando npx tsc
.
In questa documentazione è inoltre possibile trovare istruzioni sull'uso della versione più recente di TypeScript in Visual Studio Code.
Cosa c'è di nuovo in TypeScript 5.0?
In questo articolo, esploriamo 5 importanti aggiornamenti introdotti in TypeScript. Queste funzionalità includono:
Decoratori modernizzati
I decoratori sono in circolazione in TypeScript da un po' sotto una bandiera sperimentale, ma la nuova versione li aggiorna con la proposta ECMAScript, che ora è nella fase 3, il che significa che è in una fase in cui viene aggiunta a TypeScript.
I decoratori sono un modo per personalizzare il comportamento delle classi e dei loro membri in modo riutilizzabile. Ad esempio, se hai una classe che ha due metodi, greet
e getAge
:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
Nei casi d'uso del mondo reale, questa classe dovrebbe avere metodi più complicati che gestiscono una logica asincrona e hanno effetti collaterali, ecc., in cui vorresti inserire alcune chiamate console.log
per aiutare a eseguire il debug dei metodi.
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log('LOG: Method Execution Starts.'); console.log(`Hello, my name is ${this.name}.`); console.log('LOG: Method Execution Ends.'); } getAge() { console.log('LOG: Method Execution Starts.'); console.log(`I am ${this.age} years old.`); console.log('Method Execution Ends.'); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
Questo è uno schema che ricorre frequentemente e sarebbe conveniente avere una soluzione da applicare a ogni metodo.
È qui che entrano in gioco i decoratori. Possiamo definire una funzione denominata debugMethod
che appare come segue:
function debugMethod(originalMethod: any, context: any) { function replacementMethod(this: any, ...args: any[]) { console.log('Method Execution Starts.'); const result = originalMethod.call(this, ...args); console.log('Method Execution Ends.'); return result; } return replacementMethod; }
Nel codice precedente, debugMethod
prende il metodo originale ( originalMethod
) e restituisce una funzione che esegue quanto segue:
- Registra un messaggio "Inizio esecuzione metodo".
- Passa il metodo originale e tutti i suoi argomenti (incluso questo).
- Registra un messaggio "L'esecuzione del metodo termina".
- Restituisce qualsiasi valore restituito dal metodo originale.
Usando i decoratori, puoi applicare il debugMethod
ai tuoi metodi come mostrato nel codice qui sotto:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } @debugMethod greet() { console.log(`Hello, my name is ${this.name}.`); } @debugMethod getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
Questo produrrà quanto segue:
LOG: Entering method. Hello, my name is Ron. LOG: Exiting method. LOG: Entering method. I am 30 years old. LOG: Exiting method.
Quando si definisce la funzione decoratore ( debugMethod
), viene passato un secondo parametro chiamato context
(è l'oggetto context — ha alcune informazioni utili su come è stato dichiarato il metodo decorato e anche il nome del metodo). Puoi aggiornare il tuo debugMethod
per ottenere il nome del metodo dall'oggetto context
:
function debugMethod( originalMethod: any, context: ClassMethodDecoratorContext ) { const methodName = String(context.name); function replacementMethod(this: any, ...args: any[]) { console.log(`'${methodName}' Execution Starts.`); const result = originalMethod.call(this, ...args); console.log(`'${methodName}' Execution Ends.`); return result; } return replacementMethod; }
Quando esegui il codice, l'output riporterà ora il nome di ciascun metodo decorato con il decoratore debugMethod
:
'greet' Execution Starts. Hello, my name is Ron. 'greet' Execution Ends. 'getAge' Execution Starts. I am 30 years old. 'getAge' Execution Ends.
C'è di più in quello che puoi fare con i decoratori. Sentiti libero di controllare la richiesta pull originale per ulteriori informazioni su come utilizzare i decoratori in TypeScript.
Presentazione dei parametri di tipo const
Questa è un'altra grande versione che ti offre un nuovo strumento con generici per migliorare l'inferenza che ottieni quando chiami le funzioni. Per impostazione predefinita, quando dichiari i valori con const
, TypeScript deduce il tipo e non i suoi valori letterali:
// Inferred type: string[] const names = ['John', 'Jake', 'Jack'];
Fino ad ora, per ottenere l'inferenza desiderata, si doveva usare l'asserzione const aggiungendo “as const”:
// Inferred type: readonly ["John", "Jake", "Jack"] const names = ['John', 'Jake', 'Jack'] as const;
Quando chiami le funzioni, è simile. Nel codice seguente, il tipo di paesi dedotto è string[]
:
type HasCountries = { countries: readonly string[] }; function getCountriesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: string[] const countries = getCountriesExactly({ countries: ['USA', 'Canada', 'India'] });
Potresti desiderare un tipo più specifico di cui un modo per risolvere prima d'ora è stato aggiungere l'asserzione as const
:
// Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] } as const);
Questo può essere difficile da ricordare e implementare. Tuttavia, TypeScript 5.0 introduce una nuova funzionalità in cui è possibile aggiungere un modificatore const a una dichiarazione di parametro di tipo, che applicherà automaticamente un'inferenza simile a const come impostazione predefinita.
type HasCountries = { countries: readonly string[] }; function getNamesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] });
L'utilizzo di parametri di tipo const
consente agli sviluppatori di esprimere l'intento in modo più chiaro nel codice. Se una variabile deve essere costante e non cambiare mai, l'utilizzo di un parametro di tipo const
garantisce che non possa mai essere modificata accidentalmente.

È possibile controllare la richiesta pull originale per ulteriori informazioni su come funziona il parametro di tipo const in TypeScript.
Miglioramenti alle enum
Gli enum in TypeScript sono un potente costrutto che consente agli sviluppatori di definire un insieme di costanti denominate. In TypeScript 5.0, sono stati apportati miglioramenti alle enum per renderle ancora più flessibili e utili.
Ad esempio, se hai il seguente enum passato in una funzione:
enum Color { Red, Green, Blue, } function getColorName(colorLevel: Color) { return colorLevel; } console.log(getColorName(1));
Prima dell'introduzione di TypeScript 5.0, potresti passare un numero di livello errato e non genererebbe alcun errore. Ma con l'introduzione di TypeScript 5.0, genererà immediatamente un errore.
Inoltre, la nuova versione trasforma tutte le enumerazioni in enumerazioni di unione creando un tipo univoco per ogni membro calcolato. Questo miglioramento consente di restringere tutte le enumerazioni e di fare riferimento ai loro membri come tipi:
enum Color { Red, Purple, Orange, Green, Blue, Black, White, } type PrimaryColor = Color.Red | Color.Green | Color.Blue; function isPrimaryColor(c: Color): c is PrimaryColor { return c === Color.Red || c === Color.Green || c === Color.Blue; } console.log(isPrimaryColor(Color.White)); // Outputs: false console.log(isPrimaryColor(Color.Red)); // Outputs: true
Miglioramenti delle prestazioni di TypeScript 5.0
TypeScript 5.0 include numerosi cambiamenti significativi nella struttura del codice, nelle strutture dei dati e nelle estensioni algoritmiche. Ciò ha contribuito a migliorare l'intera esperienza di TypeScript, dall'installazione all'esecuzione, rendendola più veloce ed efficiente.
Ad esempio, la differenza tra le dimensioni del pacchetto di TypeScript 5.0 e 4.9 è piuttosto impressionante.
TypeScript è stato recentemente migrato dagli spazi dei nomi ai moduli, consentendogli di sfruttare strumenti di compilazione moderni in grado di eseguire ottimizzazioni come il sollevamento dell'ambito. Inoltre, la rimozione di parte del codice deprecato ha ridotto di circa 26,4 MB la dimensione del pacchetto di 63,8 MB di TypeScript 4.9.

Ecco alcune vittorie più interessanti in termini di velocità e dimensioni tra TypeScript 5.0 e 4.9:
Scenario | Tempo o dimensioni rispetto a TS 4.9 |
tempo di costruzione materiale-ui | 90% |
Orario di avvio del compilatore TypeScript | 89% |
Tempo di costruzione del drammaturgo | 88% |
Tempo di autocostruzione del compilatore TypeScript | 87% |
Tempo di compilazione di Outlook Web | 82% |
Tempo di compilazione del codice VS | 80% |
dattiloscritto npm Dimensione pacchetto | 59% |
Risoluzione bundler per una migliore risoluzione del modulo
Quando scrivi un'istruzione di importazione in TypeScript, il compilatore deve sapere a cosa si riferisce l'importazione. Lo fa usando la risoluzione del modulo. Ad esempio, quando scrivi import { a } from "moduleA"
, il compilatore deve conoscere la definizione di a
in moduleA
per verificarne l'utilizzo.
In TypeScript 4.7 sono state aggiunte due nuove opzioni per le impostazioni --module
e moduleResolution
: node16
e nodenext
.
Lo scopo di queste opzioni era rappresentare in modo più accurato le regole di ricerca esatte per i moduli ECMAScript in Node.js. Tuttavia, questa modalità ha diverse restrizioni che non vengono applicate da altri strumenti.
Ad esempio, in un modulo ECMAScript in Node.js, qualsiasi importazione relativa deve includere un'estensione di file affinché funzioni correttamente:
import * as utils from "./utils"; // Wrong import * as utils from "./utils.mjs"; // Correct
TypeScript ha introdotto una nuova strategia chiamata "moduleResolution bundler". Questa strategia può essere implementata aggiungendo il seguente codice nella sezione "compilerOptions" del file di configurazione di TypeScript:
{ "compilerOptions": { "target": "esnext", "moduleResolution": "bundler" } }
Questa nuova strategia è adatta a coloro che utilizzano bundler moderni come Vite, esbuild, swc, Webpack, Parcel e altri che utilizzano una strategia di ricerca ibrida.
Puoi controllare la richiesta pull originale e la sua implementazione per ulteriori informazioni su come funziona il bundler moduleResolution
in TypeScript.
Deprecazioni
TypeScript 5.0 viene fornito con un certo deprezzamento, inclusi i requisiti di runtime, le modifiche lib.d.ts e le modifiche di rottura dell'API.
- Requisiti di runtime: TypeScript ora ha come target ECMAScript 2018 e il pacchetto imposta un'aspettativa minima del motore di 12.20. Pertanto, gli utenti di Node.js devono disporre di una versione minima di 12.20 o successiva per utilizzare TypeScript 5.0.
- lib.d.ts Modifiche: sono state apportate alcune modifiche al modo in cui vengono generati i tipi per il DOM, che potrebbero influire sul codice esistente. In particolare, alcune proprietà sono state convertite da numeri a tipi letterali numerici e proprietà e metodi per la gestione degli eventi taglia, copia e incolla sono stati spostati tra le interfacce.
- Modifiche di rottura dell'API: alcune interfacce non necessarie sono state rimosse e sono stati apportati alcuni miglioramenti alla correttezza. Anche TypeScript 5.0 è passato ai moduli.
TypeScript 5.0 ha deprecato alcune impostazioni e i valori corrispondenti, tra cui target: ES3
, out
, noImplicitUseStrict
, keyofStringsOnly
, suppressExcessPropertyErrors
, suppressImplicitAnyIndexErrors
, noStrictGenericChecks
, charset
, importsNotUsedAsValues
e preserveValueImports
, nonché anteporre nei riferimenti al progetto.
Sebbene queste configurazioni rimarranno valide fino a TypeScript 5.5, verrà emesso un avviso per avvisare gli utenti che le stanno ancora utilizzando.
Riepilogo
In questo articolo, hai appreso alcune delle principali funzionalità e miglioramenti apportati da TypeScript 5.0, come miglioramenti agli enum, alla risoluzione del bundler e ai parametri di tipo const, insieme a miglioramenti alla velocità e alle dimensioni.
Se state pensando a TypeScript per i vostri prossimi progetti, provate gratuitamente l'hosting di applicazioni di Kinsta.
Ora è il tuo turno! Quali caratteristiche o miglioramenti trovi più interessanti in TypeScript 5.0? Ce ne sono di significativi che potremmo aver trascurato? Fateci sapere nei commenti.