Menu

Omit in TypeScript: rimuovere proprietà da un tipo (esempi)

Omit<T, K> crea un tipo con tutte le proprietà di T tranne le chiavi K. Impara Omit con una e più chiavi, come sovrascrivere il tipo di una proprietà, come rimuovere la proprietà in fase di esecuzione, Omit vs Exclude e Pick, perché Omit accetta chiavi che non esistono, un Omit rigoroso e Omit sui tipi unione.

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

Omit<T, K> è un utility type integrato che crea un tipo oggetto con tutte le proprietà di T tranne le chiavi elencate in K. L'uso classico: un tipo User che ha una password, e una versione pubblica che non ce l'ha.

PublicUser resta sincronizzato con User: aggiungi un campo a User e compare anche in PublicUser, mentre password resta fuori. Omit agisce solo sui tipi; è la destrutturazione in toPublic a rimuovere la proprietà dall'oggetto vero e proprio.

Omettere più chiavi

Il secondo argomento è un'unione, quindi elenca tutte le chiavi che ti servono con |:

"Il tipo senza i campi generati dal server" è l'Omit più comune nel codice reale: un unico tipo di modello, con Omit che ricava la forma dell'input per i moduli di creazione e le richieste API.

Come è definito Omit

Omit è costruito a partire da altri due utility type:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Leggilo dall'interno: keyof T è l'unione di tutte le chiavi, Exclude<keyof T, K> toglie da quell'unione quelle indesiderate, e Pick tiene le chiavi rimanenti. Quindi Omit<User, "password"> è Pick<User, "id" | "name" | "email">. Dato che Pick itera sulle chiavi di T, i modificatori readonly e ? delle proprietà rimanenti vengono mantenuti.

Nota il vincolo: K extends keyof any, non K extends keyof T. Questo dettaglio è l'argomento della prossima sezione.

Omit non controlla che la chiave esista

keyof any è string | number | symbol, quindi Omit accetta qualsiasi chiave, anche una che T non ha. Un errore di battitura compila e non rimuove nulla:

Nessun errore da nessuna parte, e la password finisce nel JSON. Il vincolo permissivo è stato scelto di proposito quando Omit è stato aggiunto in TypeScript 3.5. Le ragioni del team: nel codice generico le chiavi omesse spesso non provengono da T (una funzione che unisce due oggetti può tipizzare il suo argomento rest come Omit<T1, keyof T2>), nemmeno Exclude vincola il suo secondo parametro, e una versione vincolata avrebbe rotto molti dei tipi Omit scritti a mano che i progetti avevano già. Per il codice di tutti i giorni, una versione più rigorosa intercetta l'errore di battitura:

index.ts(10,36): error TS2344: Type '"pasword"' does not satisfy the constraint 'keyof User'.
index.ts(11,7): error TS2741: Property 'password' is missing in type '{ id: number; name: string; }' but required in type 'PublicUser'.

Il primo errore è quello che conta: la chiave viene controllata rispetto a keyof User. Il secondo ne è una conseguenza, dato che non è stato omesso nulla e PublicUser richiede ancora password. Correggi l'ortografia in "password" e compila. Molti codebase lo definiscono una volta in un file di tipi condiviso e lo usano al posto di Omit.

Sovrascrivere il tipo di una proprietà

Un'interfaccia non può estenderne un'altra e cambiare una proprietà in un tipo incompatibile: interface ApiUser extends User { id: string } fallisce con error TS2430: Interface 'ApiUser' incorrectly extends interface 'User'. Prima ometti la proprietà, poi dichiarala di nuovo:

Con un alias di tipo la stessa cosa è un'intersezione: type ApiUser = Omit<User, "id"> & { id: string }. Senza l'Omit, l'intersezione User & { id: string } darebbe a id il tipo number & string, cioè never.

Omit sui tipi unione

Omit non è distributivo. Su un'unione parte dal keyof dell'intera unione, che contiene solo le chiavi condivise da tutti i membri, quindi le proprietà specifiche di ciascun membro spariscono:

index.ts(8,39): error TS2353: Object literal may only specify known properties, and 'radius' does not exist in type 'NewShape'.

NewShape è diventato { kind: "circle" | "square" }. Per omettere da ciascun membro separatamente, applica Omit dentro un tipo condizionale distributivo:

T extends unknown ? ... : never è sempre vero; serve solo a far distribuire il tipo condizionale sull'unione. La pagina sui tipi condizionali spiega perché funziona. Usa DistributiveOmit ogni volta che il tipo da cui ometti è un'unione discriminata.

Omettere una proprietà annidata

Omit guarda solo le chiavi di primo livello, quindi Omit<Settings, "editor.wordWrap"> non rimuove nulla (e, come visto sopra, non si lamenta). Per togliere una proprietà di un livello più in basso, ometti il genitore e aggiungilo di nuovo con un suo Omit:

Settings["editor"] è un indexed access type: dà un nome al tipo dell'oggetto annidato così che Omit possa lavorarci.

Rimuovere proprietà in fase di esecuzione

Omit cambia solo il tipo. Per produrre un oggetto senza quelle proprietà, toglile con la destrutturazione, oppure scrivi un piccolo helper generico il cui tipo di ritorno è Omit:

L'helper vincola K a keyof T, quindi una chiave scritta male è un errore nel punto della chiamata. La copia è superficiale: gli oggetti annidati sono condivisi con l'originale.

Omit vs Pick vs Exclude

Agisce suMantieneEsempioRisultato
Omit<T, K>tipi oggettotutte le proprietà tranne KOmit<User, "password">{ id; name; email }
Pick<T, K>tipi oggettosolo le proprietà in KPick<User, "id" | "name">{ id; name }
Exclude<U, M>tipi unionei membri dell'unione non assegnabili a MExclude<"a" | "b" | "c", "a">"b" | "c"

Scegli tra Omit e Pick in base a cosa deve succedere quando il tipo di origine cresce. Omit include automaticamente le nuove proprietà, il che va bene per "tutto tranne il segreto". Pick non le include mai, il che va bene per "esattamente questi campi" ed è più sicuro per tutto ciò che esce dal tuo server. Pick controlla le sue chiavi rispetto a keyof T; Omit no.

Domande frequenti

Cosa fa Omit in TypeScript?

Omit<T, K> costruisce un nuovo tipo oggetto con tutte le proprietà di T tranne quelle le cui chiavi sono in K. Omit<User, "password"> è User senza password. Cambia solo il tipo; non rimuove nulla da un oggetto in fase di esecuzione.

Come ometto più proprietà in TypeScript?

Passa un'unione di chiavi come secondo argomento: Omit<User, "password" | "createdAt" | "updatedAt">. Vengono rimosse tutte le chiavi dell'unione.

Che differenza c'è tra Omit ed Exclude?

Omit rimuove proprietà da un tipo oggetto: Omit<{ a: 1; b: 2 }, "a"> è { b: 2 }. Exclude rimuove membri da un tipo unione: Exclude<"a" | "b", "a"> è "b". Omit è implementato con Exclude: Pick<T, Exclude<keyof T, K>>.

Perché Omit non dà errore con una chiave che non esiste?

Il suo parametro delle chiavi è vincolato a keyof any (qualsiasi string, number o symbol), non a keyof T, quindi un errore di battitura come Omit<User, "pasword"> compila e non rimuove nulla. Definisci type StrictOmit<T, K extends keyof T> = Omit<T, K> per ottenere un errore con le chiavi sconosciute.

Come rimuovo una proprietà da un oggetto in fase di esecuzione in TypeScript?

Usa la destrutturazione con un elemento rest: const { password, ...rest } = user;. TypeScript tipizza rest come l'oggetto senza password, la stessa forma di Omit<User, "password">, e la proprietà sparisce davvero dal nuovo oggetto.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA