Gli utility types sono tipi generici integrati in TypeScript che trasformano un tipo in un altro. Invece di scrivere un secondo tipo User con tutti i campi opzionali, scrivi Partial<User>; invece di copiare tre campi, Pick<User, "id" | "name">. Sono globali, quindi non serve importarli.
Quando User cambia, tutti e quattro i tipi derivati si adeguano. La libreria standard di TypeScript (lib.es5.d.ts) dichiara 22 utility types. Le sezioni seguenti li elencano tutti, raggruppati in base al genere di tipo su cui lavorano, con un link alla pagina dettagliata quando esiste.
Tipi oggetto: Partial, Required, Readonly, Pick, Omit, Record
| Utility type | Cosa fa | Esempio |
|---|---|---|
Partial<T> | rende opzionali tutte le proprietà | Partial<User> per i dati di un aggiornamento |
Required<T> | rende obbligatorie tutte le proprietà (toglie ?) | Required<Config> dopo aver applicato i valori predefiniti |
Readonly<T> | rende readonly tutte le proprietà | Readonly<State> |
Pick<T, K> | conserva solo le chiavi K | Pick<User, "id" | "name"> |
Omit<T, K> | toglie le chiavi K | Omit<User, "password"> |
Record<K, V> | un tipo oggetto con chiavi K e valori V | Record<"en" | "de", string> |
Required<T> è l'opposto di Partial<T>; la pagina su Partial tratta entrambi, compreso l'undefined esplicito che uno spread come questo lascia passare.
Tipi unione: Exclude, Extract, NonNullable
| Utility type | Cosa fa | Esempio |
|---|---|---|
Exclude<U, M> | toglie i membri dell'unione assegnabili a M | Exclude<"a" | "b" | "c", "a"> è "b" | "c" |
Extract<U, M> | conserva i membri dell'unione assegnabili a M | Extract<string | number, number> è number |
NonNullable<T> | toglie null e undefined | NonNullable<string | null> è string |
Questi tre lavorano sulle unioni, non sugli oggetti. È questa la differenza chiave rispetto a Pick e Omit, che ricevono un tipo oggetto e una lista delle sue chiavi.
Tipi di funzioni e classi: Parameters, ReturnType e altri
| Utility type | Cosa fa | Esempio |
|---|---|---|
ReturnType<F> | il tipo di ritorno di un tipo funzione | ReturnType<typeof createStore> |
Parameters<F> | i tipi dei parametri come tupla | Parameters<typeof fetchPage>[0] |
ConstructorParameters<C> | i parametri del costruttore di una classe come tupla | ConstructorParameters<typeof Point> |
InstanceType<C> | il tipo dell'istanza creata da un costruttore | InstanceType<typeof Point> |
ThisParameterType<F> | il tipo del parametro this di una funzione | ThisParameterType<typeof greet> |
OmitThisParameter<F> | il tipo funzione senza il suo parametro this | il tipo di greet.bind(obj) |
ThisType<T> | imposta il tipo di this dentro i metodi di un oggetto letterale | si usa con noImplicitThis nelle API in stile builder |
NoInfer<T> | impedisce che un parametro di tipo venga dedotto da questa posizione | fallback: NoInfer<C> |
typeof createOrder serve perché queste utility ricevono un tipo, e createOrder è un valore. Lo stesso vale per le classi: typeof Point è il tipo del costruttore, mentre Point da solo, usato come tipo, indica già il tipo dell'istanza.
NoInfer controlla da dove un generic prende il suo tipo:
Senza NoInfer, TypeScript dedurrebbe C da entrambi gli argomenti e lo allargherebbe a "red" | "green" | "blue", quindi il refuso nel valore di ripiego verrebbe accettato.
Tipi per le stringhe: Uppercase, Lowercase, Capitalize, Uncapitalize
| Utility type | Cosa fa | Esempio |
|---|---|---|
Uppercase<S> | converte in maiuscolo un tipo letterale stringa | Uppercase<"get"> è "GET" |
Lowercase<S> | lo converte in minuscolo | Lowercase<"GET"> è "get" |
Capitalize<S> | mette in maiuscolo il primo carattere | Capitalize<"name"> è "Name" |
Uncapitalize<S> | mette in minuscolo il primo carattere | Uncapitalize<"Name"> è "name" |
Questi quattro sono integrati nel compilatore invece di essere scritti in TypeScript, e danno il meglio dentro i template literal types, come `on${Capitalize<E>}` per i nomi dei gestori di eventi.
Promise: Awaited
| Utility type | Cosa fa | Esempio |
|---|---|---|
Awaited<T> | il tipo che ottieni con await, scartando le promise annidate | Awaited<Promise<Promise<number>>> è number |
Awaited<ReturnType<typeof fn>> è il modo standard per dare un nome al tipo del risultato di una funzione async senza dichiararlo a parte.
Combinare gli utility types
Gli utility types si annidano. Alcune combinazioni ricorrono abbastanza spesso da valere la pena di impararle a memoria:
Un utility type annidato si legge dall'interno verso l'esterno: Readonly<Pick<Post, "id" | "title">> prima conserva due proprietà, poi le rende di sola lettura. Con gli stessi pezzi si costruisce un helper PartialBy che rende opzionali solo alcune chiavi; la pagina su Partial lo scrive per intero.
Gli utility types non fanno nulla a runtime
Ogni utility type viene cancellato quando il codice viene compilato. Un valore tipizzato come Omit<User, "password"> può ancora contenere una password a runtime, se l'oggetto da cui proviene ne aveva una:
Il tipo limita solo ciò che il tuo codice può leggere. Per togliere un campo dai dati, estrailo con una destrutturazione come nelle ultime righe, e per impedire modifiche a runtime usa Object.freeze, non Readonly. I tipi integrati sono mapped types e conditional types di una riga, quindi con gli stessi strumenti puoi scrivere i tuoi.
Domande frequenti
Cosa sono gli utility types in TypeScript?
Tipi generici forniti con TypeScript che trasformano altri tipi: Partial<T> rende opzionali tutte le proprietà, Pick<T, K> ne conserva alcune, ReturnType<F> ricava il tipo di ritorno di una funzione, e così via. Sono dichiarati nella libreria standard, quindi li usi senza importare nulla.
Devo importare gli utility types?
No. Partial, Omit, Record, ReturnType e gli altri sono tipi globali dei file di libreria integrati in TypeScript. Scrivi Partial<User> dove vuoi; non servono import né pacchetti npm.
Quali utility types sono integrati in TypeScript?
22, tutti dichiarati in lib.es5.d.ts: Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract, NonNullable, Parameters, ConstructorParameters, ReturnType, InstanceType, ThisParameterType, OmitThisParameter, ThisType, NoInfer, Awaited, Uppercase, Lowercase, Capitalize e Uncapitalize.
Gli utility types modificano gli oggetti a runtime?
No. Descrivono solo tipi e vengono cancellati dal JavaScript generato. Omit<User, "password"> non elimina nessuna proprietà password, e Readonly<T> non congela niente. Per cambiare l'oggetto reale devi scrivere il codice: una destrutturazione con rest, Object.freeze, e così via.
Posso scrivere i miei utility types?
Sì. Quelli integrati sono normale TypeScript: la maggior parte sono mapped types o conditional types di una riga in lib.es5.d.ts. type Nullable<T> = { [K in keyof T]: T[K] | null } è un utility type personalizzato scritto allo stesso modo.