Interfacce e type alias
Interfacce e type alias
Pensa a un contratto di affitto standard. Invece di riscrivere da zero le stesse clausole ogni volta che affitti un appartamento diverso, usi un modello: cambi il nome dell'inquilino, l'indirizzo, l'importo del canone, ma la struttura del contratto — quali clausole ci sono, cosa deve contenere ogni sezione — resta la stessa, definita in un unico posto. Se un giorno la legge cambia e devi aggiungere una clausola obbligatoria, la aggiorni nel modello, non in ogni singolo contratto firmato.
TypeScript offre due meccanismi per fare esattamente questo con i tipi: dare loro un nome centrale e riutilizzabile. Si chiamano interfacce e type alias. Li studieremo entrambi in profondità, e vedremo quando ha senso usare l'uno o l'altro.
Type alias
Ripensa alla sintassi : {valuta: string, valore: number} che abbiamo usato finora per le annotazioni di tipo. Questa sintassi diventa sempre più complicata man mano che aggiungiamo proprietà al tipo. Inoltre, se passiamo oggetti di questo tipo attraverso varie funzioni e variabili, finiamo per avere un sacco di tipi che devono essere aggiornati manualmente ogni volta che serve fare una modifica!
I type alias aiutano a risolvere questo problema, permettendoci di:
- Definire un nome più significativo per questo tipo.
- Dichiarare la forma del tipo in un unico posto.
- Importare ed esportare questo tipo tra i moduli, esattamente come se fosse un valore importabile/esportabile.
// @filename: tipi.ts
export type type Importo = {
valuta: string;
valore: number;
}
Importo = {
valuta: stringvaluta: string;
valore: numbervalore: number;
};
// @filename: utilities.ts
import { type Importo = {
valuta: string;
valore: number;
}
Importo } from "./tipi";
function function stampaImporto(imp: Importo): voidstampaImporto(imp: Importoimp: type Importo = {
valuta: string;
valore: number;
}
Importo) {
var console: ConsoleThe `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(imp: Importoimp);
const { const valuta: stringvaluta, const valore: numbervalore } = imp: Importoimp;
var console: ConsoleThe `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(`${const valuta: stringvaluta} ${const valore: numbervalore}`);
}Un paio di cose da notare:
- Il tooltip su
imp, nel tuo editor, ora è molto più pulito e più semantico (significativo, legato al concetto che rappresenta) — diceImportoinvece di ripetere tutta la struttura ogni volta. - Import/export di questo
typefunziona esattamente come farebbe per una funzione o una classe in JavaScript.
È importante capire che il nome
Importoè solo per nostra comodità. Questo resta comunque un sistema di tipi strutturale, come abbiamo visto nel capitolo precedente:
// @filename: tipi.ts
export type type Importo = {
valuta: string;
valore: number;
}
Importo = {
valuta: stringvaluta: string;
valore: numbervalore: number;
};
// @filename: utilities.ts
import { type Importo = {
valuta: string;
valore: number;
}
Importo } from "./tipi";
function function stampaImporto(imp: Importo): voidstampaImporto(imp: Importoimp: type Importo = {
valuta: string;
valore: number;
}
Importo) {
var console: ConsoleThe `console` module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and
[`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module.
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for
more information.
Example using the global `console`:
```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```
Example using the `Console` class:
```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html)
(the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)).
```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```
See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.log(imp: Importoimp);
}
const const donazione: {
valuta: string;
valore: number;
descrizione: string;
}
donazione = {
valuta: stringvaluta: "EUR",
valore: numbervalore: 30.0,
descrizione: stringdescrizione: "Donazione al banco alimentare",
};
// Funziona: la "forma" corrisponde, anche se donazione ha una
// proprietà extra e non è mai stata dichiarata esplicitamente come "Importo"
function stampaImporto(imp: Importo): voidstampaImporto(const donazione: {
valuta: string;
valore: number;
descrizione: string;
}
donazione);Diamo un'occhiata per un momento alla sintassi di dichiarazione:
type Importo = {
valuta: string;
valore: number;
};Un paio di cose da segnalare:
- Questa è una delle rare occasioni in cui vediamo informazioni di tipo alla destra dell'operatore di assegnazione (
=). - Stiamo usando il
TitleCase(iniziale maiuscola) per formattare il nome dell'alias. Questa è una convenzione comune. - Come vediamo qui sotto, possiamo dichiarare un alias con un dato nome solo una volta in un determinato scope. È un po' come funziona la dichiarazione di una variabile
letoconst:
type Importo = { valuta: stringvaluta: string;
valore: numbervalore: number;
};
type Importo = { fallira: "questo non funzionerà"fallira: "questo non funzionerà";
};Un type alias può contenere qualsiasi tipo, dato che è letteralmente un alias (un nome) per un tipo di qualsiasi natura.
Ecco un esempio di come possiamo applicare tipi di ritorno espliciti alle funzioni, e poi "ripulire" un po' di codice dal capitolo precedente sugli union e intersection type, usando i type alias:
// @filename: comune.ts
export function function lanciaMoneta(): "testa" | "croce"lanciaMoneta() {
if (var Math: MathAn intrinsic object that provides basic mathematics functionality and constants.Math.Math.random(): numberReturns a pseudorandom number between 0 and 1.random() > 0.5) return "testa";
return "croce";
}
export const const successo: readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
successo = ["successo", { nome: "Davide"nome: "Davide", email: "davide@example.com"email: "davide@example.com" }] as type const = readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
const;
export const const errore: readonly ["errore", Error]errore = ["errore", new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error("Qualcosa è andato storto!")] as type const = readonly ["errore", Error]const;
// @filename: originale.ts
import { function lanciaMoneta(): "testa" | "croce"lanciaMoneta, const successo: readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
successo, const errore: readonly ["errore", Error]errore } from "./comune";
/**
* Versione ORIGINALE
*/
export function function forseRecuperaDatiUtente(): readonly ["errore", Error] | readonly ["successo", {
readonly nome: string;
readonly email: string;
}]
Versione ORIGINALEforseRecuperaDatiUtente():
| readonly ["errore", Error]
| readonly ["successo", { readonly nome: stringnome: string; readonly email: stringemail: string }] {
// implementazione identica in entrambi gli esempi
if (function lanciaMoneta(): "testa" | "croce"lanciaMoneta() === "testa") {
return const successo: readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
successo;
} else {
return const errore: readonly ["errore", Error]errore;
}
}
// @filename: con-alias.ts
import { function lanciaMoneta(): "testa" | "croce"lanciaMoneta, const successo: readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
successo, const errore: readonly ["errore", Error]errore } from "./comune";
type type EsitoErrore = readonly ["errore", Error]EsitoErrore = readonly ["errore", Error];
type type EsitoSuccesso = readonly ["successo", {
readonly nome: string;
readonly email: string;
}]
EsitoSuccesso = readonly [
"successo",
{ readonly nome: stringnome: string; readonly email: stringemail: string },
];
type type Esito = EsitoErrore | EsitoSuccessoEsito = type EsitoErrore = readonly ["errore", Error]EsitoErrore | type EsitoSuccesso = readonly ["successo", {
readonly nome: string;
readonly email: string;
}]
EsitoSuccesso;
/**
* Versione RIPULITA
*/
export function function forseRecuperaDatiUtente2(): EsitoVersione RIPULITAforseRecuperaDatiUtente2(): type Esito = EsitoErrore | EsitoSuccessoEsito {
// implementazione identica in entrambi gli esempi
if (function lanciaMoneta(): "testa" | "croce"lanciaMoneta() === "testa") {
return const successo: readonly ["successo", {
readonly nome: "Davide";
readonly email: "davide@example.com";
}]
successo;
} else {
return const errore: readonly ["errore", Error]errore;
}
}Ereditarietà nei type alias
Puoi creare type alias che combinano tipi esistenti con nuovo comportamento, usando gli intersection type (&):
type type DataSpeciale = Date & {
getDescrizione(): string;
}
DataSpeciale = Date & { function getDescrizione(): stringgetDescrizione(): string };
const const capodanno: DataSpecialecapodanno: type DataSpeciale = Date & {
getDescrizione(): string;
}
DataSpeciale = var Object: ObjectConstructorProvides functionality common to all JavaScript objects.Object.ObjectConstructor.assign<Date, {
getDescrizione: () => "Ultimo giorno dell'anno";
}>(target: Date, source: {
getDescrizione: () => "Ultimo giorno dell'anno";
}): Date & {
getDescrizione: () => "Ultimo giorno dell'anno";
} (+3 overloads)
Copy the values of all of the enumerable own properties from one or more source objects to a
target object. Returns the target object.assign(new var Date: DateConstructor
new () => Date (+4 overloads)
Date(), {
getDescrizione: () => "Ultimo giorno dell'anno"getDescrizione: () => "Ultimo giorno dell'anno",
});
const capodanno: DataSpecialecapodanno.function getDescrizione(): stringgetDescrizione();
Anche se non esiste una vera keyword extends utilizzabile quando si definiscono type alias, un intersection type ha un effetto molto simile.
Interfacce
Un'interfaccia è un modo per definire un tipo oggetto (da non confondere con un tipo chiamato object, di cui parleremo più avanti). In questo contesto, pensa a un tipo oggetto come a qualcosa che assomiglia a questo:
{
campo: "valore";
}I tipi oggetto possono essere anonimi:
function stampaImporto(imp: { valuta: string; valore: number }) {}oppure descritti usando un type alias, come abbiamo appena visto:
type Importo = {
valuta: string;
valore: number;
};string | number è un esempio di qualcosa che non può essere descritto usando un tipo oggetto, perché fa uso dell'operatore union.
Come i type alias, le interfacce possono essere importate/esportate tra moduli proprio come i valori, e servono a dare un "nome" a un tipo specifico.
interface Importo {
valuta: string;
valore: number;
}
function stampaImporti(imp: Importo) {
imp.valuta; // ✅ autocompletamento disponibile qui
}Ereditarietà nelle interfacce
extends
Se hai mai visto una classe JavaScript che "eredita" comportamento da una classe base, hai visto un esempio di quella che TypeScript chiama una heritage clause (clausola di ereditarietà): extends.
function consumaCibo(arg) {}
class AnimaleCheMangia {
mangia(cibo) {
consumaCibo(cibo);
}
}
class Gatto extends AnimaleCheMangia {
miagola() {
return "miao";
}
}
const g = new Gatto();
g.mangia; // ereditato da AnimaleCheMangia
g.miagola(); // "miao"- Esattamente come in JavaScript, una sottoclasse fa
extendsda una classe base. - In aggiunta, una "sotto-interfaccia" fa
extendsda un'interfaccia base, come mostrato nell'esempio qui sotto:
interface Animale {
Animale.isAlive(): booleanisAlive(): boolean;
}
interface Mammifero extends Animale {
Mammifero.getColorePelo(): stringgetColorePelo(): string;
}
interface Criceto extends Mammifero {
Criceto.squittisce(): stringsquittisce(): string;
}
function function accudisciCriceto(c: Criceto): voidaccudisciCriceto(c: Cricetoc: Criceto) {
c: Cricetoc.Mammifero.getColorePelo(): stringgetColorePelo(); // ereditato da Mammifero
c: Cricetoc.Criceto.squittisce(): stringsquittisce(); // definito su Criceto
c: Cricetoc.Animale.isAlive(): booleanisAlive(); // ereditato da Animale, tramite Mammifero
}implements
TypeScript aggiunge una seconda heritage clause che può essere usata per dichiarare che una data classe deve produrre istanze conformi a una data interfaccia: implements.
function function consumaCibo(arg: unknown): voidconsumaCibo(arg: unknownarg: unknown) {}
interface AnimaleGenerico {
AnimaleGenerico.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown): void;
}
class Cane implements AnimaleGenerico { Cane.abbaia(): stringabbaia() {
return "bau";
}
}Nell'esempio sopra, vediamo che TypeScript protesta perché non abbiamo aggiunto un metodo mangia() alla nostra classe Cane. Senza questo metodo, le istanze di Cane non sono conformi all'interfaccia AnimaleGenerico. Aggiorniamo il codice:
function function consumaCibo(arg: unknown): voidconsumaCibo(arg: unknownarg: unknown) {}
interface AnimaleGenerico {
AnimaleGenerico.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown): void;
}
class class CaneCane implements AnimaleGenerico {
Cane.abbaia(): stringabbaia() {
return "bau";
}
Cane.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown) {
function consumaCibo(arg: unknown): voidconsumaCibo(cibo: unknowncibo);
}
}Ecco, ora va meglio. Anche se TypeScript (e JavaScript) non supportano una vera ereditarietà multipla (estendere più di una classe base), questa keyword implements ci dà la capacità di validare, a compile time, che le istanze di una classe siano conformi a uno o più "contratti" (tipi). Nota che extends e implements possono essere usati insieme:
function function consumaCibo(arg: unknown): voidconsumaCibo(arg: unknownarg: unknown) {}
class class OrganismoViventeOrganismoVivente {
OrganismoVivente.isAlive(): booleanisAlive() {
return true;
}
}
interface AnimaleGenerico {
AnimaleGenerico.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown): void;
}
interface PuoAbbaiare {
PuoAbbaiare.abbaia(): stringabbaia(): string;
}
class class Cane2Cane2 extends class OrganismoViventeOrganismoVivente implements AnimaleGenerico, PuoAbbaiare {
Cane2.abbaia(): stringabbaia() {
return "bau";
}
Cane2.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown) {
function consumaCibo(arg: unknown): voidconsumaCibo(cibo: unknowncibo);
}
}Anche se è possibile usare implements con un type alias...
function function consumaCibo(arg: unknown): voidconsumaCibo(arg: unknownarg: unknown) {}
type type PuoSaltare = {
saltaAdAltezza(): number;
}
PuoSaltare = {
function saltaAdAltezza(): numbersaltaAdAltezza(): number;
};
class class Cane3Cane3 implements type PuoSaltare = {
saltaAdAltezza(): number;
}
PuoSaltare {
Cane3.saltaAdAltezza(): numbersaltaAdAltezza() {
return 1.7;
}
Cane3.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown) {
function consumaCibo(arg: unknown): voidconsumaCibo(cibo: unknowncibo);
}
}se il tipo dovesse mai violare le "regole del tipo oggetto", ci sono alcuni potenziali problemi...
function function consumaCibo(arg: unknown): voidconsumaCibo(arg: unknownarg: unknown) {}
type type PuoSaltare = string | {
saltaAdAltezza(): number;
}
PuoSaltare = { function saltaAdAltezza(): numbersaltaAdAltezza(): number } | string;
class class Cane3Cane3 implements PuoSaltare { Cane3.abbaia(): stringabbaia() {
return "bau";
}
Cane3.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown) {
function consumaCibo(arg: unknown): voidconsumaCibo(cibo: unknowncibo);
}
}Questi tipi di errori iniziano a comparire nel punto concettualmente equivalente al call site, piuttosto che al declaration site, il che non è l'ideale.
Per questo motivo, è preferibile usare le interfacce per i tipi che vengono usati con la heritage clause implements.
Interfacce "aperte"
Le interfacce di TypeScript sono "aperte", il che significa che, a differenza dei type alias, puoi avere dichiarazioni multiple con lo stesso nome nello stesso scope:
interface AnimaleGenerico {
// da prima
AnimaleGenerico.mangia(cibo: unknown): voidmangia(cibo: unknowncibo: unknown): void;
}
function function nutri(animale: AnimaleGenerico): voidnutri(animale: AnimaleGenericoanimale: AnimaleGenerico) {
animale: AnimaleGenericoanimale.AnimaleGenerico.mangia(cibo: unknown): voidmangia; // disponibile
animale: AnimaleGenericoanimale.AnimaleGenerico.isAlive(): booleanisAlive; // disponibile grazie alla seconda dichiarazione qui sotto
}
// SECONDA DICHIARAZIONE DELLO STESSO NOME
interface AnimaleGenerico {
AnimaleGenerico.isAlive(): booleanisAlive(): boolean;
}Queste dichiarazioni vengono unite (merge) per creare un risultato identico a quello che vedresti se i metodi isAlive e mangia fossero entrambi su un'unica dichiarazione di interfaccia.
Ti starai chiedendo: dove e come è utile questa cosa?
Caso d'uso: estendere tipi nativi o di librerie esistenti
Immagina una situazione in cui vuoi aggiungere una proprietà globale all'oggetto window:
window.document; // una proprietà già esistente
window.exampleProperty = 42;
// ❌ Property 'exampleProperty' does not exist on type 'Window & typeof globalThis'.Se aggiungiamo una dichiarazione che aumenta l'interfaccia Window esistente, dicendo a TypeScript che quella proprietà esiste, l'errore sparisce:
// diciamo a TS che "exampleProperty" esiste
interface Window {
exampleProperty: number;
}
window.document; // una proprietà già esistente
window.exampleProperty = 42; // ✅ ora funzionaQuello che abbiamo fatto qui è estendere un'interfaccia Window esistente, che TypeScript ha già configurato per noi dietro le quinte.
Scegliere tra type e interface
In molte situazioni, sia un alias type che un'interface andrebbero benissimo, tuttavia...
- Se devi definire qualcosa di diverso da un tipo oggetto (per esempio, usando l'operatore union
|), devi usare un type alias. - Se devi definire un tipo da usare con la heritage clause
implementssu unaclass, usa un'interfaccia. - Se devi permettere a chi consuma i tuoi tipi di estenderli, devi usare un'interfaccia.
Ricorsione
I tipi ricorsivi sono auto-referenziali, e vengono spesso usati per descrivere tipi annidabili all'infinito. Per esempio, considera array di numeri annidabili all'infinito:
[3, 4, [5, 6, [7], 59], 221];Potresti leggere o vedere indicazioni secondo cui devi usare una combinazione di interface e type per i tipi ricorsivi. Dalla versione 3.7 di TypeScript, questo è molto più semplice, e funziona sia con i type alias che con le interfacce:
type type NumeriAnnidati = number | NumeriAnnidati[]NumeriAnnidati = number | type NumeriAnnidati = number | NumeriAnnidati[]NumeriAnnidati[];
const const val: NumeriAnnidatival: type NumeriAnnidati = number | NumeriAnnidati[]NumeriAnnidati = [3, 4, [5, 6, [7], 59], 221];
if (typeof const val: NumeriAnnidati[]val !== "number") {
const val: NumeriAnnidati[]val.Array<NumeriAnnidati>.push(...items: NumeriAnnidati[]): numberAppends new elements to the end of an array, and returns the new length of the array.push(41); // OK
const val: NumeriAnnidati[]val.Array<NumeriAnnidati>.push(...items: NumeriAnnidati[]): numberAppends new elements to the end of an array, and returns the new length of the array.push("questo non funzionerà");}