Interface TypeScript, dalla sintassi a interface vs type
Interface TypeScript
Interface TypeScript: sintassi, extends, merging, vs type | We.Inc
Crea un'interface campo per campo, guarda il type alias corrispondente e impara membri opzionali e readonly, extends, declaration merging e quando un type alias è la scelta migliore.
Un'interface dà un nome alla forma di un oggetto: quali proprietà ha e che tipo contiene ciascuna. Rendi una proprietà opzionale con ? e immutabile con readonly, costruisci su un'altra forma con extends e riapri lo stesso nome per aggiungere altri membri. Le interface non lasciano traccia nel JavaScript compilato; servono solo a guidare il type checker.
La sintassi delle interface in sintesi
Vuoi
Scrivi
Una proprietà obbligatoria
name: string;
Una proprietà opzionale
nickname?: string;
Una proprietà che non può essere riassegnata
readonly id: number;
Un metodo
greet(other: User): string;
Un numero qualsiasi di chiavi stringa
[key: string]: number;
Riusare un'altra forma
interface Admin extends User { ... }
Combinare più forme
interface A extends B, C { ... }
Aggiungi campi nel generatore qui sotto, spunta opzionale o readonly e copia l'interface o il type alias equivalente. Le sezioni successive trattano l'estensione, il merging e i pochi casi in cui la scelta tra interface e type conta davvero.
Dai un nome all'interface e, se si basa su un'altra, scrivi quel nome nella casella extends.
Aggiungi una riga per ogni proprietà, scegliendo il tipo e se è opzionale o readonly.
Leggi l'interface e il type alias che il generatore scrive a partire dalle stesse righe.
Copia una delle due versioni nel tuo codice.
Parti da interface per le forme degli oggetti e passa a type quando ti serve una union, una tupla o un mapped type. È la regola pratica suggerita dallo stesso handbook di TypeScript.
readonly impedisce solo di riassegnare quella singola proprietà. Gli oggetti annidati al suo interno possono comunque cambiare, e nulla viene imposto a runtime.
Una proprietà opzionale si legge come il suo tipo oppure undefined, quindi controllala prima di usarla o dalle un valore predefinito nella destrutturazione.
Usa nomi di interface al singolare e descrittivi, come Invoice o CartItem. Un prefisso come IInvoice non aggiunge nulla che l'editor non ti dica già.
Domande frequenti
Devo usare interface o type?
Per le forme degli oggetti funzionano entrambi e l'handbook lo definisce soprattutto una preferenza personale, suggerendo interface finché non ti serve una funzionalità che ha solo type. Usa type per union, tuple, primitivi e mapped o conditional type; usa interface quando vuoi il declaration merging.
Cos'è il declaration merging?
Dichiarare un'interface con un nome già esistente aggiunge membri a quella esistente invece di dare errore. Le librerie ci contano per permetterti di estendere i loro tipi, per esempio aggiungendo una proprietà a Window. Un type alias con un nome ripetuto è un errore.
Cosa succede se due interface unite danno tipi diversi a una proprietà?
Il compilatore segnala un errore. Con un'intersezione di due tipi il conflitto non viene segnalato nella dichiarazione; la proprietà diventa never, cosa che emerge più tardi ed è più difficile da rintracciare.
Un'interface può descrivere una funzione o un array?
Sì. Una call signature come (value: string): boolean descrive una funzione, e una index signature come [index: number]: string descrive un oggetto simile a un array. Nei casi semplici un type alias come type Check = (v: string) => boolean si legge meglio.
Le interface esistono a runtime?
No. Compilano in niente, quindi non puoi usare instanceof con un'interface né elencarne le chiavi. Controlla i valori con una guardia a runtime e annota il suo tipo di ritorno con l'interface.
extends e intersezioni a confronto
interface Bear extends Animal copia ogni membro di Animal e ti permette di aggiungerne altri. Un type alias fa lo stesso lavoro con un'intersezione, type Bear = Animal & { honey: boolean }.
L'handbook osserva che extends può essere più veloce per il compilatore e che segnala i membri in conflitto proprio dove sono dichiarati. Le intersezioni sono comode per unire forme che non controlli tu.
Opzionali, readonly e index signature
Un punto interrogativo dopo il nome rende una proprietà opzionale, e il suo tipo in lettura include undefined. readonly blocca l'assegnazione a quella proprietà dopo la creazione, ma è solo un controllo in fase di compilazione.
Una index signature, [key: string]: number, descrive dizionari le cui chiavi non puoi elencare in anticipo. Le proprietà con nome nella stessa interface devono essere compatibili con il tipo dei valori della index signature.
Il merging per estendere i tipi globali e delle librerie
Dato che le interface si uniscono, un file di dichiarazione può aggiungere i tuoi campi alle forme integrate. Un caso comune è dare a Window una proprietà impostata da uno script, così leggerla supera il type check senza un cast.
interface vs type alias
Cosa può fare ciascuna dichiarazione
Funzionalità
type alias
Descrivere la forma di un oggetto
Sì
Sì
Aggiungere membri ridichiarando il nome
Sì (merging)
No, errore di identificatore duplicato
Costruire su un'altra forma
Intersezione con &
Union di forme o letterali
No
Sì
Dare un nome a un primitivo o a una tupla
No
Sì
Mapped e conditional type
No
Sì
Pattern che scriverai spesso
Estendere una e più interface
Admin ottiene ogni membro di User più i propri; Card combina due forme non correlate.
Declaration merging su Window
Inserito in un file .d.ts, permette a window.appVersion di superare il type check ovunque.
Index signature e call signature
Scores accetta qualsiasi chiave stringa; Validator descrive un valore invocabile.
Generatore di interface
Nome dell'interface
Extends (facoltativo)
Nome della proprietà {n}
Tipo della proprietà {n}
Opzionale
Readonly
Rimuovi la proprietà {n}
Aggiungi proprietà
Come interface
Come type alias
Copia l'interface
Copia il type
I nomi devono iniziare con una lettera, $ o _ e usare solo lettere, cifre, $ o _.