Best practice nella programmazione TypeScript
Questo articolo spiega le best practice nella programmazione TypeScript.
Questa guida spiega le best practice pratiche per sfruttare i tipi di TypeScript, ridurre gli errori e scrivere codice più leggibile.
YouTube Video
Best practice nella programmazione TypeScript
Il più grande vantaggio di TypeScript è 'prevenire bug tramite i tipi e rendere chiara l'intenzione del codice'.
Le best practice non sono semplici regole, ma un insieme di principi per scrivere codice sicuro, leggibile e manutenibile. Di seguito, presentiamo le best practice più comuni di TypeScript con esempi pratici.
Evita any e assegna sempre ai tipi definizioni significative
Per prima cosa, vediamo il punto di 'evitare any e assegnare tipi significativi.'.
any disattiva completamente il controllo dei tipi, vanificando lo scopo di usare TypeScript. Invece di usare any solo per far funzionare le cose, è importante fornire tipi il più possibile descrittivi.
1// Bad
2function parse(data: any) {
3 return data.value;
4}Questo codice può accettare qualsiasi valore, quindi non è possibile prevenire errori in fase di esecuzione.
1// Good
2type ParsedData = {
3 value: string;
4};
5
6function parse(data: ParsedData): string {
7 return data.value;
8}Definendo i tipi, chiarisci l'intento degli input e output e migliori la sicurezza.
Definisci sempre esplicitamente le strutture degli oggetti utilizzando type o interface.
Successivamente, vediamo il punto di 'definire sempre esplicitamente le strutture degli oggetti usando type o interface.'.
Se usi oggetti ad-hoc, la loro struttura può diventare ambigua. Estrai sempre i tipi per la riusabilità e la manutenibilità.
1// Bad
2function createUser(user: { name: string; age: number }) {
3 console.log(user.name);
4}Anche in piccoli frammenti di codice, è importante sviluppare l'abitudine di separare i tipi.
1// Good
2type User = {
3 name: string;
4 age: number;
5};
6
7function createUser(user: User): void {
8 console.log(user.name);
9}Assegnando nomi ai tipi, diventa molto più facile comprendere l'intera base di codice.
Usa i tipi unione per rappresentare con precisione tutti gli stati possibili.
Se usi tipi string o number grezzi per le condizioni, valori inattesi possono passare. Utilizzando i tipi unione, puoi rappresentare solo gli stati consentiti a livello di tipo.
1// Bad
2function setStatus(status: string) {
3 console.log(status);
4}In questo codice, stringhe prive di significato o valori errati non possono essere rilevati in fase di compilazione.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4function setStatus(status: Status): void {
5 console.log(status);
6}Utilizzando i tipi unione, puoi eliminare in modo affidabile gli 'stati impossibili' durante la compilazione. Di conseguenza, la sicurezza delle diramazioni condizionali e l'affidabilità del codice miglioreranno.
Gestisci esplicitamente null e undefined.
Poi, vediamo il punto di 'gestire esplicitamente null e undefined.'.
In TypeScript, è importante esprimere la possibile assenza di un valore nel tipo. Se lasci tutto ambiguo, potresti avere errori in fase di esecuzione.
1type User = {
2 name: string;
3 email?: string;
4};email potrebbe non esistere, quindi dovresti gestirla con questa ipotesi.
1function printEmail(user: User): void {
2 if (user.email) {
3 console.log(user.email);
4 }
5}Controlla sempre i valori opzionali prima di usarli.
Usa le asserzioni di tipo (as) solo come ultima risorsa.
Successivamente, vediamo il punto di 'non abusare delle asserzioni di tipo.'.
Le asserzioni di tipo aggirano temporaneamente il controllo dei tipi di TypeScript per dichiarare: 'So che questo valore è di questo tipo.'. Un uso eccessivo compromette la sicurezza dei tipi.
1// Bad
2const value = input as string;In questo codice, anche se il valore reale non è una stringa, non si verifica alcun errore, il che può causare errori in fase di esecuzione. Come mostrato nel codice seguente, scegli di controllare in sicurezza utilizzando prima i type guard.
1// Good
2function isString(value: unknown): value is string {
3 return typeof value === "string";
4}
5
6if (isString(input)) {
7 console.log(input.toUpperCase());
8}I type guard sono un meccanismo per determinare in modo sicuro un tipo mentre si verifica il valore reale. Dando priorità ai type guard rispetto alle asserzioni di tipo, puoi prevenire più facilmente errori a runtime.
Non affidarti troppo all'inferenza dei tipi per i tipi di ritorno.
Poi, vediamo il punto di 'non affidarsi troppo all'inferenza per i tipi di ritorno.'.
L'inferenza dei tipi di TypeScript è potente, ma è più sicuro annotare esplicitamente i tipi di ritorno delle funzioni pubbliche. Questo minimizza l'impatto potenziale di cambi futuri.
1// Bad
2function sum(a: number, b: number) {
3 return a + b;
4}Dichiara chiaramente la tua intenzione anche nelle funzioni più semplici.
1// Good
2function sum(a: number, b: number): number {
3 return a + b;
4}Specificare i tipi di ritorno aumenta la stabilità delle tue API.
Gestisci in sicurezza gli input usando unknown.
Successivamente, vediamo il punto di 'accettare in modo sicuro input esterno usando unknown.'.
Per input esterni come API, JSON o input dell'utente, utilizza unknown invece di any. Facendo così, ti assicuri che tutti i valori siano validati, mantenendo la sicurezza dei tipi.
1// Bad
2function handleResponse(data: any) {
3 console.log(data.id);
4}Ecco come validare i tipi con unknown.
1// Good
2function handleResponse(data: unknown): void {
3 if (
4 typeof data === "object" &&
5 data !== null &&
6 "id" in data
7 ) {
8 console.log((data as { id: number }).id);
9 }
10}unknown non può essere usato così com'è; è un tipo che richiede validazione. È particolarmente efficace quando si gestiscono input esterni.
Aumenta l'espressività combinando tipi piccoli.
Poi, vediamo il punto di 'aumentare l'espressività combinando tipi più piccoli.'.
Definire grandi tipi tutti in una volta riduce la leggibilità e la manutenibilità. Dividi i tuoi tipi in unità significative e combinaili quando necessario.
1type Id = number;
2
3type UserProfile = {
4 id: Id;
5 name: string;
6};
7
8type UserWithStatus = UserProfile & {
9 status: "active" | "inactive";
10};Trattare i tipi come componenti aiuta a organizzare il tuo design.
type e interface
Vantaggi di interface
type e interface possono entrambi definire tipi, ma i loro usi e caratteristiche sono differenti. Usandoli per i ruoli appropriati, l'intenzione delle definizioni dei tuoi tipi diventa più chiara.
1// Bad
2type User = {
3 id: number;
4 name: string;
5};
6
7type AdminUser = {
8 id: number;
9 name: string;
10 role: "admin";
11};Se duplichi parti comuni in questo modo, il tuo codice diventa fragile ai cambiamenti.
1// Good
2interface User {
3 id: number;
4 name: string;
5}
6
7interface AdminUser extends User {
8 role: "admin";
9}interface è ideale per progetti che prevedono estensioni (extends) ed è adatto a esprimere la 'forma' degli oggetti.
Vantaggi di type
D'altra parte, type è più espressivo e adatto a gestire tipi unione e intersezione.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4type ApiResponse<T> =
5 | { status: "success"; data: T }
6 | { status: "error"; message: string };type è adatto a esprimere stati, opzioni e combinazioni.
Linee guida per la scelta tra type e interface
Come regola generale, usa interface per strutture e contratti di oggetti, e type quando hai bisogno dell'espressività di unioni, intersezioni o operazioni sui tipi.
Entrambi funzionano in modo simile, ma è importante scegliere in base a ciò che comunica il motivo dell'esistenza del tipo.
Considera i tuoi tipi come documentazione.
Infine, vediamo il punto di 'scrivere i tipi come documentazione.'.
Buone definizioni di tipi trasmettono più informazioni di quanto possano fare i commenti. È importante mirare a una situazione in cui 'la specifica possa essere compresa semplicemente guardando i tipi.'.
1type ApiError = {
2 code: number;
3 message: string;
4 retryable: boolean;
5};In questo modo, uno dei principali punti di forza di TypeScript è che le definizioni di tipi possono funzionare come una sorta di documento di specifica.
Riepilogo
Le best practice di TypeScript non mirano a essere eccessivamente restrittive. L'essenza è chiarire l'intenzione tramite i tipi e scrivere codice che sia robusto ai cambiamenti.
Accumulando piccole regole durante lo sviluppo quotidiano, puoi ottenere effetti a lungo termine come 'revisioni più semplici', 'meno bug' e 'migliore comprensione per te e per gli altri in futuro'.
Per prima cosa, chiedendoti 'come posso esprimere questo tramite i tipi?', scriverai codice di alta qualità in stile TypeScript.
Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.