Menu

Type inference in TypeScript: quando annotare e quando no

TypeScript ricava la maggior parte dei tipi dai valori che scrivi. Scopri cosa deduce per variabili, let e const, oggetti, array e valori di ritorno, come le callback ricevono i tipi dal contesto e dove serve ancora un'annotazione.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

La type inference è TypeScript che ricava un tipo che non hai scritto. Inizializza una variabile e prende il tipo del valore; restituisci un valore da una funzione e il tipo di ritorno segue. La maggior parte del codice TypeScript contiene molte meno annotazioni di quanto si pensi.

I commenti mostrano ciò che un editor visualizza quando passi il mouse su ogni nome. L'ultima riga è un errore di compilazione (TS2322) anche se count non è mai stato annotato; // @ts-expect-error la segna come attesa, così il resto del blocco viene eseguito.

let e const: allargamento dei letterali

Una const non può mai cambiare, quindi TypeScript le dà il tipo più stretto: il valore letterale stesso. Una let riceve il tipo più ampio, perché potrebbe essere riassegnata in seguito.

let a = "left";    // string
const b = "left";  // "left"
let c = 42;        // number
const d = 42;      // 42
const e = true;    // true

Questo conta quando una funzione accetta solo certi valori:

index.ts(7,6): error TS2345: Argument of type 'string' is not assignable to parameter of type '"left" | "right"'.

Risolvi con const, oppure annotando la variabile: let dir: "left" | "right" = "left". I tipi letterali hanno una pagina tutta loro, literal types.

Oggetti e array allargano i loro membri

Le proprietà di un oggetto letterale sono modificabili, quindi si allargano come let, anche quando l'oggetto stesso è const. Gli elementi degli array fanno lo stesso. as const rende tutto readonly e mantiene i tipi letterali.

Inferenza del tipo di ritorno

Il tipo di ritorno di una funzione viene dedotto dalle sue istruzioni return. Quando ce ne sono diverse, il risultato è la loro union.

I tipi di ritorno dedotti vanno bene per la maggior parte delle funzioni. Scrivi il tipo di ritorno in modo esplicito sulle funzioni esportate o pubbliche quando vuoi che la firma sia un contratto: una modifica nel corpo che restituisce un tipo diverso fallisce allora nella funzione, non in qualche chiamante lontano.

Contextual typing: le callback ricevono i loro tipi

I parametri non vengono dedotti dal modo in cui una funzione viene chiamata. Ci sono due eccezioni. Un parametro con un valore predefinito prende il tipo di quel valore (function retry(times = 3) rende times un number). E una funzione scritta dove è già atteso un tipo funzione riceve i tipi dei parametri da quel contesto, quindi i parametri delle callback non hanno bisogno di annotazioni.

Lo stesso vale per forEach, filter, reduce, le callback delle Promise, i gestori di eventi e qualsiasi funzione passata a un parametro con un tipo funzione dichiarato.

I parametri vanno annotati

Senza un valore predefinito o un contesto di questo tipo, TypeScript non guarda come viene chiamata una funzione per indovinare i tipi dei parametri. Con strict attivo, un parametro non annotato è l'errore TS7006:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

Scrivi function double(x: number) e il blocco stampa 42, con il tipo di ritorno number dedotto da x * 2.

Best common type

Per un array letterale con valori di tipo diverso, TypeScript deduce un array della union dei loro tipi. Non cerca un tipo base comune che non hai indicato.

L'array vuoto è l'unico caso a cui fare attenzione. const list = [] parte come array "in evoluzione", il cui tipo cresce a ogni push; un'annotazione, const list: string[] = [], dichiara subito l'intenzione ed è più chiara.

Quando annotare

SituazioneAnnotare?
Variabile inizializzata con un valoreNo, il tipo lo dà il valore
Variabile dichiarata senza valore (let result;)Sì, let result: string;
Parametri di funzioneSì (tranne le callback con contesto o un valore predefinito)
Tipo di ritorno di una piccola funzione internaFacoltativo
Tipo di ritorno di una funzione esportata o pubblicaConsigliato, come contratto
Array o oggetto vuoto da riempire in seguitoSì, const ids: number[] = []
Il valore deve mantenere i suoi tipi letteraliUsa as const
Il valore deve corrispondere a un tipo ma mantenere il proprio tipo dedottoUsa satisfies

Il modo più rapido per vedere cosa è stato dedotto è passare il mouse sul nome nel tuo editor. Senza un editor, assegna il valore a una variabile di un tipo sbagliato, come const probe: boolean = mixed;, e il messaggio di errore del compilatore indica il tipo dedotto.

Domande frequenti

Cos'è la type inference in TypeScript?

È il compilatore che ricava un tipo che non hai scritto. let count = 0 dà a count il tipo number, e una funzione che restituisce a + b su due numeri riceve il tipo di ritorno number. Il tipo dedotto viene controllato esattamente come uno scritto.

Devo annotare ogni variabile in TypeScript?

No. Annota i parametri delle funzioni (non vengono dedotti dalle chiamate, solo da un valore predefinito o dal contesto di una callback), i tipi di ritorno delle funzioni esportate se vuoi un'API stabile e le variabili dichiarate senza valore. Lascia che TypeScript deduca il resto: const name: string = "Ada" ripete ciò che il compilatore sa già.

Perché const deduce un tipo letterale mentre let deduce string?

Una const non può mai essere riassegnata, quindi const dir = "left" riceve il tipo letterale "left". Una let potrebbe contenere in seguito qualsiasi altra stringa, quindi let dir = "left" viene allargato a string. Lo stesso allargamento avviene per le proprietà di un oggetto letterale, perché le proprietà si possono riassegnare; as const lo impedisce.

Perché TypeScript dice che un parametro ha implicitamente il tipo 'any'?

È l'errore TS7006 di noImplicitAny, che fa parte di strict. TypeScript non deduce i tipi dei parametri dal modo in cui una funzione viene chiamata, quindi un parametro senza tipo sarebbe any. Aggiungi un'annotazione, function double(x: number), oppure passa la funzione dove il suo tipo è noto dal contesto, per esempio come callback di map.

Come vedo il tipo che TypeScript ha dedotto?

Passa il mouse sul nome nel tuo editor (VS Code e la maggior parte degli editor mostrano il tipo dedotto in un tooltip). Nel codice, assegnare il valore a una variabile di un tipo palesemente sbagliato fa stampare al compilatore il tipo dedotto nel messaggio di errore.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA