ProgrammazioneSviluppatore Fullstack

Come funziona Exclude in TypeScript, quando usarlo per manipolare i tipi union e quali sono le sfide nel lavorare con questo tipo utilitario?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Exclude<T, U> è un tipo utilitario introdotto in TypeScript per sottrarre un tipo da un altro, quando è necessario escludere alcuni valori da un tipo union.

Storia della questione

Inizialmente in TypeScript non c'era un modo comodo per sottrarre un tipo da un altro. Nella creazione di API generiche, così come durante il refactoring, era spesso necessario ottenere un tipo "residuo" — tutto tranne i valori vietati. Piuttosto che eseguire manualmente le manipolazioni con union, era necessario mantenere più interfacce simili.

Problema

Ad esempio, quando si ha un tipo 'A | B | C', ma si desidera ottenere un tipo senza B. Questo è spesso necessario nella costruzione di parametri di ingresso complessi per le funzioni, nella filtrazione dei valori consentiti e nella formazione dinamica dei tipi.

Soluzione

Exclude risolve questo problema. La sua firma semplificata è la seguente:

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

Restituisce un tipo che esclude da T tutti i membri di U.

Esempio:

type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK

Caratteristiche chiave:

  • Permette di formare tipi dinamici tramite la "sottrazione" di parti dall'union.
  • Semplifica il Refactoring — quando si modifica il tipo di base, tutte le derivazioni si aggiornano automaticamente.
  • Può essere utilizzato, ad esempio, per filtrare casi di switch o chiavi di oggetti.

Domande insidiose.

Si può usare Exclude per tipi normali, e non union?

Se T non è un tipo union, ma è incluso in U — Exclude funzionerà comunque, ma il risultato potrebbe essere never o T, il che non è sempre intuitivo.

Exclude<'a', 'a'> // risultato: never Exclude<'a', 'b'> // risultato: 'a'

Rimuove Exclude tutte le menzioni di un tipo nella struttura dell'oggetto?

No, Exclude non passa ricorsivamente attraverso i campi nidificati del tipo, esclude solo al livello superiore dell'union.

Come funziona Exclude con interfacce e tipi-oggetto?

Confronta l'intero tipo, non le singole proprietà. Pertanto, Exclude da un union di diverse interfacce rimuove solo quelle che corrispondono completamente a U.

interface A { x: number }; interface B { y: string }; // Exclude<A|B, B> restituisce: A (B corrisponde completamente)

Errori tipici e anti-pattern

  • Tentativi di applicare Exclude a corrispondenze nidificate o parziali
  • Utilizzo per "rimuovere" proprietà dell'interfaccia, piuttosto che varianti dell'union
  • Ignorare la possibilità di ottenere il tipo never per corrispondenze complete dei tipi

Esempio dalla vita reale

Caso negativo

Validazione dei ruoli utente tramite Exclude<UserRoles, 'admin'>, ma si è dimenticato che Exclude non si applica alle strutture nidificate — i diritti 'admin:sub' non erano stati esclusi.

Pro:

  • Semplicità nella formazione del tipo dei ruoli.

Contro:

  • Comportamento non ovvio con tipi nidificati o simili; ruolo critico mancante.

Caso positivo

Utilizzo di Exclude per limitare l'API pubblica sulle azioni: Exclude<Action, 'delete'>, che esclude l'operazione pericolosa.

Pro:

  • Sicurezza a livello di tipizzazione, non è possibile chiamare un'azione vietata.

Contro:

  • Necessità di mantenere aggiornati gli elenchi dei tipi base.