Hai mai scritto un oggetto in TypeScript per scoprire a runtime che una proprietà manca, o peggio, che il tipo accetta qualsiasi cosa? Il problema nasce dall'uso di any o di interfacce troppo generiche. Gli Utility Types sono la soluzione integrata in TypeScript per trasformare i tipi esistenti senza riscriverli. Noi, di Meteora Web, li usiamo ogni giorno per ridurre gli errori a compile time e rendere il codice auto-documentato. Vediamo come funzionano e quando applicarli.
Cosa sono gli Utility Types e perché usarli in TypeScript?
Gli Utility Types sono tipi generici predefiniti che manipolano la struttura di altri tipi. Ti permettono di creare varianti parziali, obbligatorie, immutabili o selezionate di interfacce e tipi. Il vantaggio concreto? Meno bug in produzione, codice più leggibile e manutenibile. Non devi scrivere a mano versioni alternative di ogni interfaccia. Usare Utility Types significa spostare la logica di validazione dal runtime al compile time, dove gli errori costano zero invece di far perdere fatturato.
Come funziona Partial e quando conviene?
Partial<T> rende tutte le proprietà di T opzionali. È perfetto per aggiornamenti parziali di un oggetto, come un form che modifica solo alcuni campi di un profilo utente.
Sponsored Protocol
interface User {
id: number;
name: string;
email: string;
}
function updateUser(id: number, changes: Partial<User>): void {
// changes può contenere solo name, solo email, o entrambi}
Esempio reale: se stai sviluppando un backend per un e-commerce, Partial ti evita di passare l'intero oggetto prodotto per modificare solo il prezzo. Ne abbiamo parlato anche nella nostra Guida TypeScript da Zero ad Avanzato.
Pick vs Omit: qual è la differenza pratica?
Pick<T, K> sceglie solo le proprietà specificate, Omit<T, K> le esclude. La scelta dipende da cosa è più facile elencare: ciò che ti serve o ciò che vuoi togliere.
interface Product {
id: number;
name: string;
price: number;
description: string;
stock: number;
}
// Per una scheda prodotto pubblica, solo nome e prezzo
type PublicProduct = Pick<Product, 'name' | 'price'>;
// Per un report interno, tutto tranne description
type InventoryReport = Omit<Product, 'description'>;
Noi scegliamo Pick quando le proprietà da includere sono poche (es. DTO per API pubbliche) e Omit quando dobbiamo escludere dati sensibili come password o campi tecnici.
Sponsored Protocol
Quando usare Required invece di Partial?
Required<T> è l'opposto di Partial: tutte le proprietà diventano obbligatorie. Utile dopo una validazione che garantisce che i campi opzionali siano stati forniti.
interface DraftOrder {
items: string[];
coupon?: string;
}
// Dopo la validazione, il coupon deve essere presente
type ConfirmedOrder = Required<DraftOrder>;
Se lavori con form multi-step, Required ti assicura che lo step finale contenga tutto prima dell'invio. Lo stesso approccio lo applichiamo nei progetti che seguiamo in contabilità: prima di emettere una fattura, tutti i campi devono essere confermati.
Readonly: come proteggere i dati immutabili?
Readonly<T> rende ogni proprietà in sola lettura. Previene modifiche accidentali, ideale per configurazioni, costanti o snapshot di stato.
interface AppConfig {
apiUrl: string;
timeout: number;
}
const config: Readonly<AppConfig> = {
apiUrl: 'https://api.meteoraweb.com',
timeout: 5000,
};
// config.timeout = 6000; // Errore: non si può modificare
Nella nostra esperienza, le PMI sottovalutano il rischio di modifiche involontarie a configurazioni critiche. Readonly è un guardrail che costa zero performance.
Sponsored Protocol
Record: come creare oggetti con chiavi tipizzate?
Record<K, T> crea un tipo oggetto dove le chiavi sono di tipo K e i valori di tipo T. Perfetto per mappe, dizionari o cache.
type UserId = string;
type UserCache = Record<UserId, { name: string; lastLogin: Date }>;
const cache: UserCache = {
'abc123': { name: 'Mario', lastLogin: new Date() },
};
Quando dobbiamo gestire un magazzino (ricordiamo l'ERP di Hibrido Abbigliamento), usiamo Record per mappare SKU a quantità, con controllo statico su tutti i possibili identificatori.
Utility Types TypeScript — Come combinarli per scenari reali?
Il vero potere emerge quando combini Utility Types. Un caso classico: aggiornamento parziale di un sottoinsieme di proprietà in un contesto readonly.
interface Order {
id: number;
status: 'pending' | 'shipped' | 'delivered';
items: string[];
total: number;
}
// Solo alcune proprietà modificabili in modo parziale
type UpdateOrderRequest = Partial<Pick<Order, 'status' | 'items'>>;
// Stato immutabile dell'ordine dopo la conferma
type ConfirmedOrder = Readonly<Required<Pick<Order, 'id' | 'total'>>>;
Questi pattern riducono le duplicazioni e aumentano la coerenza. In ogni nostro progetto — da Laravel a Vue — li applichiamo per gestire form, API request/response e dati di configurazione. Più tipi statici = meno sorprese in produzione.
Sponsored Protocol
Cosa fare adesso
- Apri un progetto TypeScript e applica
Partialsu almeno una funzione che accetta aggiornamenti parziali. Sostituisci l'uso dianyconPartial<Interfaccia>. - Sostituisci le mappe dichiarate come
{ [key: string]: T }conRecord<string, T>per avere chiavi tipizzate e inferenza automatica. - Proteggi le tue configurazioni con
Readonlye verifica che il compilatore blocchi modifiche involontarie. - Leggi la documentazione ufficiale su Utility Types di TypeScript per esplorare anche
NonNullable,ExtractedExclude.