value as Type è una type assertion: dice a TypeScript di trattare value come Type. Molti la chiamano cast, ma è solo un'istruzione per il compilatore. Viene eliminata dall'output JavaScript, non converte nulla e a runtime non controlla nulla.
Questo è l'uso tipico: sai di un valore più di quanto possa sapere il compilatore (qui, la forma di un JSON) e lo dichiari. Se sbagli, niente ti avvisa. Le sezioni seguenti mostrano cosa significa e quando un controllo a runtime è la scelta migliore.
as e la sintassi con le parentesi angolari
La stessa asserzione si può scrivere in due modi:
const someValue: unknown = "hello";
const a = someValue as string; // as syntax
const b = <string>someValue; // angle-bracket syntax, same meaning
La forma con le parentesi angolari non è ammessa nei file .tsx, dove <string> verrebbe letto come un tag JSX. Usa as ovunque e il problema non si pone. Le asserzioni hanno una precedenza bassa, quindi racchiudile tra parentesi quando prosegui l'espressione: (value as string).length.
Le asserzioni non convertono i valori
Questa è la parte che causa bug veri. Un'asserzione cambia ciò che il compilatore crede di un valore, non il valore stesso:
Il compilatore crede che asserted sia un number, quindi asserted + 1 supera il controllo come operazione aritmetica. A runtime è ancora la stringa "42" e JavaScript concatena. Per cambiare il tipo di un valore, convertilo: Number(x), String(x), Boolean(x), BigInt(x), new Date(x). La pagina da stringa a numero confronta le funzioni di conversione.
| Vuoi | Scrivi | Effetto a runtime |
|---|---|---|
| Comunicare al compilatore un tipo che conosci | x as T | nessuno |
| Trasformare una stringa in un numero | Number(x), parseInt(x, 10) | converte |
| Trasformare qualsiasi cosa in una stringa | String(x), `${x}` | converte |
| Controllare prima il tipo | un type guard, typeof, instanceof | controlla |
Cosa permette il compilatore
as non è illimitato. TypeScript permette x as T quando uno dei due tipi è assegnabile all'altro: allargare ("a" as string, dog as Animal) e restringere (animal as Dog, unknown as User) vanno bene entrambi. Quando i tipi non si sovrappongono affatto, lo rifiuta:
Il compilatore segnala index.ts(3,11): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Il messaggio stesso indica la scappatoia: input as unknown as number. Quella doppia asserzione compila, ed è sbagliata a runtime esattamente come l'esempio qui sopra. Quando senti il bisogno di usarla, di solito la soluzione giusta è una conversione (Number(input)) o un tipo diverso.
La regola della sovrapposizione è permissiva con gli oggetti. Un oggetto letterale che ha alcune delle proprietà viene accettato, ed è così che as fa passare in silenzio oggetti incompleti:
Un'annotazione (const draft: User = { name: "Ada" }) o satisfies User segnalerebbe l'email mancante (TS2741). Usa as su un oggetto letterale solo quando intendi davvero completarlo più avanti, e preferisci costruire l'oggetto completo.
as const è diverso
as const sembra un'asserzione ma fa il contrario di allentare i controlli: rende un letterale il più stretto possibile. Le stringhe restano tipi letterali, gli array diventano tuple readonly e le proprietà degli oggetti diventano readonly.
È sicuro, perché descrive esattamente il letterale invece di affermare qualcosa che il compilatore non può vedere. (Il sizes as readonly string[] dentro isSize è un'asserzione che allarga il tipo, anch'essa sicura: permette a includes di accettare qualsiasi stringa.) Approfondisci nei literal types.
Quando un type guard è lo strumento migliore
as è un'affermazione; un type guard è un controllo. Al confine in cui i dati arrivano da fuori dal tuo codice (JSON, fetch, localStorage, input dell'utente, un messaggio), l'affermazione può essere falsa, e un'asserzione trasforma un errore chiaro al confine in uno confuso da qualche altra parte.
Una guida di massima agli strumenti che si somigliano:
| Strumento | Controlla in compilazione | Controlla a runtime | Usalo quando |
|---|---|---|---|
Annotazione const x: T = ... | sì, completamente | no | costruisci tu il valore |
satisfies T | sì, completamente, mantiene il tipo dedotto | no | oggetti letterali, configurazione |
as T | solo "i tipi si sovrappongono?" | no | ne sai più del compilatore |
x! | rimuove solo null / undefined | no | sai che un valore è impostato |
Type guard x is T | il corpo del guard è codice normale | sì | dati che arrivano da fuori |
Restano due buoni usi di as: restringere qualcosa che il compilatore non riesce a seguire (una voce di una Map impostata due righe prima, un valore da una libreria senza tipi) e il codice di test che costruisce fixture parziali. Tienili piccoli e vicini al punto in cui sai che l'affermazione è vera.
Domande frequenti
Cosa fa as in TypeScript?
value as Type è una type assertion: dice al compilatore di trattare value come Type da quel punto in poi. Viene rimossa dal JavaScript compilato, quindi non esegue alcuna conversione né alcun controllo a runtime. Se l'asserzione è sbagliata, il programma fallisce più avanti, nel punto in cui si usa il tipo sbagliato.
Come faccio un cast di tipo in TypeScript?
TypeScript non ha cast a runtime. Usa as (o la vecchia forma <Type>value) per cambiare il tipo statico quando ne sai più del compilatore. Per convertire davvero un valore, chiama una funzione: Number("42"), String(42), Boolean(x), new Date(text).
Cosa significa "as unknown as" in TypeScript?
È una doppia asserzione. TypeScript rifiuta x as T quando i due tipi non si sovrappongono affatto (errore TS2352), e passare prima per unknown aggira quel controllo, perché qualsiasi cosa si può asserire verso e da unknown. Disattiva del tutto il controllo dei tipi per quel valore, quindi riservala ai test e al codice in cui hai verificato il tipo in un altro modo.
Che differenza c'è tra as e le parentesi angolari in TypeScript?
Nessuna nel significato: <string>value e value as string sono la stessa asserzione. La forma con le parentesi angolari non si può usare nei file .tsx perché va in conflitto con JSX, quindi as è la forma che usano tutti.
Che differenza c'è tra as e satisfies?
as sostituisce il tipo dedotto e controlla molto poco (le proprietà mancanti sono ammesse). satisfies controlla il valore rispetto a un tipo, segnalando le proprietà mancanti o in più, e mantiene il tipo preciso dedotto. Preferisci satisfies per gli oggetti letterali e usa as solo quando ne sai davvero più del compilatore.