Nella lezione precedente abbiamo visto alcuni Utility Types che permettono di modificare la struttura di un tipo esistente. TypeScript ne mette a disposizione molti altri, utili per creare tipi a partire da oggetti, funzioni e unioni di tipi.
In questa lezione vedremo Record, ReturnType, Parameters, Exclude ed Extract.
| Tipo | Descrizione |
Record | Crea un tipo con proprietà definite. |
ReturnType | Ricava il tipo restituito da una funzione. |
Parameters | Ricava i tipi dei parametri di una funzione. |
Exclude | Esclude tipi da un'unione. |
Extract | Seleziona tipi da un'unione. |
Vediamoli in dettaglio.
Record<K, T> permette di creare un tipo oggetto in cui:
K rappresenta il tipo delle chiavi.T rappresenta il tipo dei valori.Ad esempio, possiamo creare un tipo Utenti:
type Utenti = Record<string, string>;Questo tipo rappresenta un oggetto con chiavi di tipo string e valori anch'essi di tipo string.
Possiamo quindi scrivere:
const utenti: Utenti = {
mario: "Andrea Rossi",
luca: "Luca Bianchi",
anna: "Anna Verdi"
};K non deve necessariamente essere string. Possiamo utilizzare un'unione di tipi per definire un insieme preciso di chiavi. Ad esempio:
type Giorno = "lunedi" | "martedi" | "mercoledi";
type Orari = Record<Giorno, string>;Ora un oggetto Orari deve contenere tutte queste chiavi:
const orari: Orari = {
lunedi: "09:00",
martedi: "10:00",
mercoledi: "09:30"
};Questo rende Record particolarmente utile quando vogliamo associare un insieme noto di chiavi a valori dello stesso tipo.
ReturnType<T> permette di ottenere automaticamente il tipo restituito da una funzione.
Supponiamo di avere una funzione creaUtente():
function creaUtente() {
return {
id: 1,
nome: "Andrea"
};
}Possiamo utilizzare:
type Utente = ReturnType<typeof creaUtente>;Il risultato sarà equivalente a:
type Utente = {
id: number;
nome: string;
};In questo modo non dobbiamo riscrivere manualmente il tipo restituito dalla funzione.
ReturnType riceve come parametro il tipo della funzione, non il suo risultato.
Per ottenere il tipo della funzione creaUtente utilizziamo typeof creaUtente e quindi ReturnType<typeof creaUtente>.
Possiamo utilizzare il tipo ottenuto normalmente:
const utente: Utente = {
id: 2,
nome: "Luca"
};Se in futuro cambiamo il valore restituito dalla funzione, il tipo generato da ReturnType si aggiornerà automaticamente.
Parameters<T> permette di estrarre i tipi dei parametri di una funzione e di ottenere una tupla.
Ad esempio:
function creaUtente(nome: string, eta: number) {
console.log(nome, eta);
}Possiamo ottenere i tipi dei suoi parametri con:
type ParametriUtente = Parameters<typeof creaUtente>;Il risultato sarà equivalente a:
type ParametriUtente = [nome: string, eta: number];In pratica, Parameters ci restituisce una tupla che contiene i tipi dei parametri nello stesso ordine in cui sono definiti nella funzione.
Possiamo utilizzare il tipo generato per descrivere un array o tupla compatibile con i parametri della funzione:
const parametri: ParametriUtente = ["Andrea", 30];Usare invece [30, "Marco"] genera un errore perché i tipi sono nell'ordine sbagliato.
Exclude<T, U> permette di rimuovere da un'unione tutti i tipi compatibili con U.
Per esempio:
type Colore = "rosso" | "verde" | "blu";Possiamo eliminare "verde":
type AltriColori = Exclude<Colore, "verde">;Il risultato sarà:
type AltriColori = "rosso" | "blu";Possiamo quindi scrivere:
let colore: AltriColori;
colore = "rosso";
colore = "blu";Se invece scriviamo colore = "verde", il codice genererà un errore.
Possiamo rimuovere più elementi contemporaneamente:
type Colore = "rosso" | "verde" | "blu" | "giallo";
type ColoriPrimari = Exclude<Colore, "verde" | "giallo">;Il risultato sarà:
type ColoriPrimari = "rosso" | "blu";Exclude è quindi utile quando vogliamo creare una nuova unione di tipi partendo da una esistente e rimuovendo determinati valori.
Extract<T, U> esegue l'operazione inversa rispetto a Exclude: invece di rimuovere i tipi compatibili con U, mantiene soltanto quelli compatibili.
Ad esempio:
type Tipo = string | number | boolean;Possiamo estrarre soltanto string e number:
type StringaONumero = Extract<Tipo, string | number>;Il risultato sarà:
type StringaONumero = string | number;Il boolean viene escluso perché non è compatibile con string | number.
Possiamo utilizzare Extract anche con un'unione di valori letterali:
type Stato = "attivo" | "inattivo" | "sospeso";
type StatoAttivo = Extract<Stato, "attivo" | "sospeso">;Il risultato diventerà:
type StatoAttivo = "attivo" | "sospeso";Possiamo quindi utilizzare StatoAttivo per accettare soltanto quei valori:
let stato: StatoAttivo;
stato = "attivo";
stato = "sospeso";Gli Utility Types possono essere combinati per creare tipi più complessi.
Per esempio, possiamo utilizzare ReturnType insieme a Partial:
function creaUtente() {
return {
id: 1,
nome: "Andrea",
email: "[email protected]"
};
}
type Utente = ReturnType<typeof creaUtente>;
type ModificaUtente = Partial<Utente>;Utente viene ricavato automaticamente dal valore restituito dalla funzione, mentre Partial ne crea una versione con tutte le proprietà opzionali.
Possiamo quindi scrivere:
const modifiche: ModificaUtente = {
email: "[email protected]"
};In questo modo, non abbiamo dovuto duplicare la struttura dell'utente.