Menu

Exclude, Extract e NonNullable in TypeScript spiegati

Exclude, Extract e NonNullable filtrano i membri di un tipo union. Scopri cosa tiene e cosa rimuove ciascuno, come scegliere i membri di una union in base alla forma, come sono costruiti a partire dai tipi condizionali e in cosa differiscono da Omit e Pick.

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

Exclude<T, U> rimuove membri da un tipo union, Extract<T, U> tiene solo i membri corrispondenti e NonNullable<T> rimuove null e undefined. Tutti e tre filtrano union; non toccano le proprietà degli oggetti.

Come ogni tipo di utilità, esistono solo in compilazione. L'ultima riga viene rifiutata dal compilatore; il JavaScript che viene eseguito non ha idea di cosa sia Visible.

Exclude, Extract e NonNullable in breve

TipoTieneEsempioRisultato
Exclude<T, U>i membri di T non assegnabili a UExclude<"a" | "b" | "c", "a">"b" | "c"
Extract<T, U>i membri di T assegnabili a UExtract<"a" | "b" | "c", "a" | "z">"a"
NonNullable<T>tutto tranne null e undefinedNonNullable<string | null | undefined>string

"Assegnabile a U" è l'espressione chiave. U non deve essere per forza un elenco di membri esatti; può essere un tipo più largo in cui rientrano diversi membri:

Scegliere i membri di una union per forma

Su una discriminated union, Extract con una forma parziale estrae le varianti che la possiedono. È il modo abituale per dare un nome a una variante di una union che non hai scritto tu:

La forma { type: "click" } è un supertipo solo della variante click, quindi è quella che Extract tiene. Una forma che corrisponde a più varianti (per esempio { x: number } se due la avessero) le tiene tutte.

NonNullable

NonNullable<T> è la versione a livello di tipi di un controllo !== null && !== undefined. Compare quando ricavi un tipo da qualcosa di facoltativo:

User["manager"] è { name: string } | null | undefined (il ? aggiunge undefined), e NonNullable li toglie entrambi. Da TypeScript 5.5 il compilatore deduce da solo anche quel predicato, quindi raw.filter((s) => s != null) è già tipizzato string[]; l'esplicito s is NonNullable<typeof s> lascia visibile l'intenzione e funziona nelle versioni più vecchie. La sua definizione è type NonNullable<T> = T & {}: il tipo oggetto vuoto {} accetta ogni valore tranne null e undefined, quindi intersecarlo rimuove esattamente quelli.

Come sono costruiti

Exclude ed Extract sono tipi condizionali di una riga nella libreria standard:

type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;

Quando T è una union, un tipo condizionale viene applicato a ogni membro separatamente (si distribuisce), e i membri never spariscono dal risultato. Quindi Exclude<"a" | "b", "a"> diventa ("a" extends "a" ? never : "a") | ("b" extends "a" ? never : "b"), cioè never | "b", cioè "b". La pagina sui tipi condizionali tratta la distribuzione nel dettaglio.

Exclude vs Omit, Extract vs Pick

I nomi si confondono facilmente perché entrambe le coppie "rimuovono" o "tengono" qualcosa. La differenza sta in ciò su cui lavorano:

Lavora suRimuove o tieneEsempio
Exclude<T, U>una unionmembri della unionExclude<"a" | "b", "a"> → "b"
Extract<T, U>una unionmembri della unionExtract<"a" | "b", "a"> → "a"
Omit<T, K>un tipo oggettoproprietàOmit<User, "password">
Pick<T, K>un tipo oggettoproprietàPick<User, "name">

Usare Exclude su un tipo oggetto è una svista comune, e fallisce in silenzio:

Account non è assegnabile a "password", quindi Exclude lo tiene intero. Omit stesso è costruito con Exclude applicato alle chiavi: Omit<T, K> è Pick<T, Exclude<keyof T, K>>. Vedi Omit per saperne di più.

Un Exclude più rigoroso

Il secondo parametro di Exclude accetta qualsiasi tipo, quindi un errore di battitura non rimuove nulla e non solleva errori. Aggiungere un vincolo fa verificare al compilatore che ciò che escludi sia davvero nella union:

index.ts(7,37): error TS2344: Type '"drafft"' does not satisfy the constraint 'Status'.

Correggi l'ortografia e compila, con Strict uguale a "published" | "archived". Lo stesso trucco funziona per Extract e per un Omit rigoroso (K extends keyof T).

Domande frequenti

Cosa fa Exclude in TypeScript?

Exclude<T, U> rimuove dalla union T ogni membro assegnabile a U. Exclude<"a" | "b" | "c", "a"> è "b" | "c". Per rimuovere più membri, passa una union come U: Exclude<T, "a" | "b">.

Che differenza c'è tra Exclude ed Extract?

Sono opposti. Exclude<T, U> tiene i membri di T che non sono assegnabili a U; Extract<T, U> tiene solo quelli che lo sono. Per gli stessi T e U, i due risultati insieme formano T.

Che differenza c'è tra Exclude e Omit?

Exclude lavora su un tipo union e rimuove membri della union. Omit lavora su un tipo oggetto e rimuove proprietà. Exclude<User, "password"> non fa nulla di utile (restituisce User), mentre Omit<User, "password"> dà il tipo oggetto senza quella proprietà. Omit è costruito su Exclude: esclude la chiave da keyof T.

Cosa fa NonNullable in TypeScript?

NonNullable<T> rimuove null e undefined da un tipo: NonNullable<string | null | undefined> è string. È definito come T & {}, perché {} accetta ogni valore tranne null e undefined.

Perché Exclude non segnala un errore per un membro che non esiste?

Il suo secondo parametro non ha vincoli, quindi accetta qualsiasi tipo, e un errore di battitura come Exclude<Status, "drafft"> non rimuove nulla in silenzio. Definisci una versione più rigorosa, type StrictExclude<T, U extends T> = Exclude<T, U>, per far verificare al compilatore che U faccia parte di T.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA