Menu

Literal types e as const in TypeScript, con esempi

Un literal type è un tipo con un solo valore, come "GET" o 404. Scopri i literal types di stringhe, numeri e booleani, le unioni di letterali, perché let allarga il tipo e const no, cosa fa as const, e i const type parameters.

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

Un literal type è un tipo con un solo valore: "up" è un tipo il cui unico membro è la stringa "up", e 404 è un tipo il cui unico membro è il numero 404. Da soli non sono molto utili. Uniti in un'unione, ti danno una variabile che accetta un insieme fisso di valori e nient'altro.

Senza il commento @ts-expect-error, l'ultima chiamata è un errore in fase di compilazione (TS2345). Con il commento, il programma compila e la chiamata viene comunque eseguita e stampa moving north by 1: i literal types esistono solo per il compilatore, e a runtime il valore è una stringa normale.

Letterali stringa, numerici e booleani

Qualsiasi valore stringa, numerico, bigint o booleano si può scrivere come tipo. Il compilatore accetta poi solo quel valore esatto.

let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;

type Port = 80 | 443 | 8080;
const port: Port = 443;

boolean stesso non è altro che l'unione true | false, ed è per questo che restringere un boolean con if (flag) lascia false nel ramo else.

Literal typeAmmetteTipo più ampio
"GET"solo la stringa "GET"string
404solo il numero 404number
10nsolo il bigint 10bigint
truesolo trueboolean

Unioni di letterali

L'uso comune è un'unione che elenca tutti i valori ammessi. Dentro la funzione il compilatore restringe l'unione man mano che la controlli, quindi ogni ramo sa esattamente quale valore ha.

Un'unione di letterali stringa è l'alternativa abituale a un enum in TypeScript. Non costa niente a runtime, i valori sono stringhe normali che puoi stampare nei log e inviare in JSON, e un errore di battitura è un errore di compilazione. Trovi di più su pro e contro in enums.

Widening: let vs const

Quando TypeScript deduce un tipo da un letterale, guarda se il valore può cambiare. Una variabile const non può mai essere riassegnata, quindi mantiene il literal type. Una variabile let sì, quindi il suo tipo viene allargato al tipo generale.

Passa il mouse su ogni nome nell'editor per vedere il tipo dedotto. Se vuoi un let che contenga solo certi valori, annotalo: let mode: "light" | "dark" = "light".

Perché le proprietà degli oggetti vengono allargate

Le proprietà di un oggetto letterale sono modificabili, quindi vengono allargate anche loro, pure quando l'oggetto è memorizzato in una const. È il modo più comune di imbattersi per caso nei literal types:

Il compilatore segnala:

index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

req viene dedotto come { url: string; method: string } perché più avanti del codice potrebbe eseguire req.method = "DELETE". Ci sono tre soluzioni:

Una quarta opzione è satisfies, che controlla l'oggetto rispetto a un tipo mantenendo i literal types delle sue proprietà.

as const

as const è una const assertion. Mettila dopo un'espressione e il compilatore deduce il tipo più stretto possibile:

  • i valori stringa, numerici e booleani mantengono i loro literal types
  • le proprietà degli oggetti diventano readonly
  • gli array letterali diventano tuple readonly di lunghezza fissa

L'asserzione vale solo in fase di compilazione. Il JavaScript emesso è lo stesso oggetto letterale senza as const, quindi niente impedisce ad altro codice di modificarlo a runtime. Se ti serve una garanzia a runtime, chiama anche Object.freeze.

Un tipo unione da un array as const

Un pattern frequente è tenere i valori ammessi in un solo array, su cui puoi iterare a runtime, e ricavarne il tipo unione. (typeof arr)[number] significa "il tipo di un elemento qualsiasi di arr".

Senza as const, ROLES sarebbe string[] e Role sarebbe una semplice string. Il cast a readonly string[] in isRole serve perché includes su una tupla di letterali accetta solo quei letterali, e lo scopo della funzione è testare una stringa che potrebbe non esserlo. Lo stesso pattern con un oggetto funziona per le mappe chiave/valore: const Status = { Active: "active", Banned: "banned" } as const, poi type Status = (typeof Status)[keyof typeof Status].

const type parameters

Una funzione generica di norma allarga i letterali che le passi. Da TypeScript 5.0 puoi marcare un parametro di tipo come const, e il compilatore dedurrà l'argomento come se avesse as const, senza chiedere a chi chiama di scriverlo.

È soprattutto uno strumento per chi scrive librerie: definizioni di route, builder e helper per schemi lo usano perché chi chiama ottenga tipi precisi da semplici letterali.

I significati di const

La parola chiave const compare in quattro posti diversi nel codice TypeScript:

SintassiTipoCosa fa
const x = 1dichiarazione JavaScriptil binding non può essere riassegnato; un valore letterale mantiene il suo literal type
expr as constasserzione TypeScripttipo più stretto: letterali, proprietà readonly, tuple readonly
function f<const T>()parametro di tipo TypeScriptdeduce gli argomenti come se avessero as const
const enum E {}enum TypeScriptun enum i cui membri vengono sostituiti in linea in fase di compilazione

Nessuno di questi congela un oggetto a runtime. const obj = { a: 1 } permette ancora obj.a = 2; è un errore solo riassegnare obj stesso.

Domande frequenti

Cos'è un literal type in TypeScript?

Un tipo che ammette un solo valore. "GET" è un tipo il cui unico valore è la stringa "GET", 404 è un tipo il cui unico valore è il numero 404, e true è un tipo il cui unico valore è true. Sono più utili combinati in unioni, come type Method = "GET" | "POST".

Cosa fa as const in TypeScript?

as const è una const assertion. Dice al compilatore di dedurre il tipo più stretto possibile per un'espressione: i valori stringa e numerici mantengono i loro literal types, le proprietà degli oggetti diventano readonly, e gli array letterali diventano tuple readonly. Cambia solo il tipo; a runtime il valore è lo stesso oggetto o array semplice, e non viene congelato.

Perché TypeScript deduce string invece del mio letterale?

Perché il valore è modificabile. let x = "a" e la proprietà in { method: "GET" } possono essere riassegnate più avanti, quindi TypeScript le allarga a string. Una variabile const mantiene il literal type "a". Per mantenere i letterali dentro un oggetto, annotalo con il literal type, usa as const, oppure usa satisfies.

Qual è la differenza tra const e as const?

const è una dichiarazione JavaScript: la variabile non può essere riassegnata, ma l'oggetto a cui punta può ancora essere modificato. as const è una type assertion di TypeScript: rende ogni proprietà del valore readonly e letterale nel sistema dei tipi. Nessuno dei due congela l'oggetto a runtime; per quello usa Object.freeze.

Come si ottiene un tipo unione da un array di stringhe?

Dichiara l'array con as const, poi indicizza il suo tipo con number: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; dà "admin" | "user". Senza as const l'array è string[] e il risultato è solo string.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA