Generics
Generics
Immagina un archivista che deve riorganizzare una fila di scatoloni numerati in un archivio con scaffali etichettati per codice. Il procedimento che segue è sempre lo stesso, indipendentemente dal fatto che gli scatoloni contengano fatture, contratti o fotografie: prende ogni scatolone, legge il codice identificativo, lo colloca sullo scaffale con quell'etichetta. Il "procedimento di riorganizzazione" è generico — funziona con qualsiasi tipo di contenuto — ma l'archivista non perde mai traccia di cosa c'è dentro ogni scatolone: se erano fatture, restano fatture anche una volta archiviate.
I generics ci permettono di parametrizzare i tipi, il che sblocca una grande opportunità di riutilizzare i tipi in modo ampio in un progetto TypeScript — esattamente come la procedura di archiviazione del nostro archivista, che funziona con qualsiasi tipo di documento senza mai perdere l'informazione su cosa contiene ogni scatolone.
Questo è un concetto piuttosto astratto, quindi partiamo da un esempio pratico concreto.
Un caso d'uso motivante
In un capitolo precedente, abbiamo parlato del concetto di strutture dati a dizionario, che potevamo tipizzare usando le index signature:
const numeriTelefono: {
[k: string]: {
idCliente: string;
prefisso: string;
numero: string;
};
} = {};
numeriTelefono.casa;
numeriTelefono.lavoro;
numeriTelefono.fax;
numeriTelefono.cellulare;Diamo per assodato che a volte è più comodo organizzare le collezioni come dizionari chiave-valore, e altre volte è più comodo usare array o liste.
Sarebbe utile avere una specie di utility che ci permetta di convertire una "lista di cose" in un "dizionario di cose" — esattamente come il nostro archivista che converte una fila di scatoloni numerati in uno scaffale organizzato per codice.
Prendiamo quindi questo array di oggetti come punto di partenza:
const listaNumeri = [
{ idCliente: "0001", prefisso: "321", numero: "123-4566" },
{ idCliente: "0002", prefisso: "174", numero: "142-3626" },
{ idCliente: "0003", prefisso: "192", numero: "012-7190" },
{ idCliente: "0005", prefisso: "402", numero: "652-5782" },
{ idCliente: "0004", prefisso: "301", numero: "184-8501" },
];... e questo è quello che vogliamo ottenere alla fine:
const dizionarioNumeri = {
"0001": { idCliente: "0001", prefisso: "321", numero: "123-4566" },
"0002": { idCliente: "0002", prefisso: "174", numero: "142-3626" },
/*... e così via */
};Alla fine, speriamo di arrivare a una soluzione che funzioni per qualsiasi lista vogliamo trasformare nell'equivalente dizionario — non solo per questo caso specifico.
Ci servirà prima di tutto un modo per produrre la "chiave" per ogni oggetto che incontriamo nell'array listaNumeri. Per restare flessibili, progetteremo la nostra funzione in modo che chiunque chieda la conversione da lista a dizionario debba anche fornire una funzione che possiamo usare per ottenere una "chiave" da ogni elemento della lista.
Forse la firma della nostra funzione assomiglierebbe a questa:
interface InfoTelefono {
InfoTelefono.idCliente: stringidCliente: string;
InfoTelefono.prefisso: stringprefisso: string;
InfoTelefono.numero: stringnumero: string;
}
function function listaADizionario(lista: InfoTelefono[], generaId: (arg: InfoTelefono) => string): {
[k: string]: InfoTelefono;
}
listaADizionario(
lista: InfoTelefono[]lista: InfoTelefono[], // prende la lista come argomento
generaId: (arg: InfoTelefono) => stringgeneraId: (arg: InfoTelefonoarg: InfoTelefono) => string, // una callback per ottenere gli id
): { [k: string]: InfoTelefono } { // restituisce un dizionario
}Ovviamente vediamo un messaggio d'errore per come stanno le cose ora, perché non abbiamo ancora implementato la funzione.
Non è troppo difficile da implementare. Costruiamo subito una soluzione molto specifica con un forEach — che rifattorizzeremo e generalizzeremo nel prossimo passaggio.
interface InfoTelefono {
InfoTelefono.idCliente: stringidCliente: string;
InfoTelefono.prefisso: stringprefisso: string;
InfoTelefono.numero: stringnumero: string;
}
function function listaADizionario(lista: InfoTelefono[], generaId: (arg: InfoTelefono) => string): {
[k: string]: InfoTelefono;
}
listaADizionario(
lista: InfoTelefono[]lista: InfoTelefono[],
generaId: (arg: InfoTelefono) => stringgeneraId: (arg: InfoTelefonoarg: InfoTelefono) => string,
): { [k: stringk: string]: InfoTelefono } {
// crea un dizionario vuoto
const const dizionario: {
[k: string]: InfoTelefono;
}
dizionario: { [k: stringk: string]: InfoTelefono } = {};
// scorri l'array
lista: InfoTelefono[]lista.Array<InfoTelefono>.forEach(callbackfn: (value: InfoTelefono, index: number, array: InfoTelefono[]) => void, thisArg?: any): voidPerforms the specified action for each element in an array.forEach((elemento: InfoTelefonoelemento) => {
const const chiaveDizionario: stringchiaveDizionario = generaId: (arg: InfoTelefono) => stringgeneraId(elemento: InfoTelefonoelemento);
const dizionario: {
[k: string]: InfoTelefono;
}
dizionario[const chiaveDizionario: stringchiaveDizionario] = elemento: InfoTelefonoelemento; // memorizza l'elemento sotto quella chiave
});
// restituisci il dizionario
return const dizionario: {
[k: string]: InfoTelefono;
}
dizionario;
}
const const listaNumeri: {
idCliente: string;
prefisso: string;
numero: string;
}[]
listaNumeri = [
{ idCliente: stringidCliente: "0001", prefisso: stringprefisso: "321", numero: stringnumero: "123-4566" },
{ idCliente: stringidCliente: "0002", prefisso: stringprefisso: "174", numero: stringnumero: "142-3626" },
];
const const risultato: {
[k: string]: InfoTelefono;
}
risultato = function listaADizionario(lista: InfoTelefono[], generaId: (arg: InfoTelefono) => string): {
[k: string]: InfoTelefono;
}
listaADizionario(const listaNumeri: {
idCliente: string;
prefisso: string;
numero: string;
}[]
listaNumeri, (item: InfoTelefonoitem) => item: InfoTelefonoitem.InfoTelefono.idCliente: stringidCliente);
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 risultato: {
[k: string]: InfoTelefono;
}
risultato);Prova questa soluzione nel TypeScript Playground: dovresti vedere che funziona per il nostro esempio specifico.
Ora proviamo a generalizzarla, in modo che funzioni per liste e dizionari del nostro tipo InfoTelefono, ma anche per tanti altri tipi. Che ne dici se sostituissimo ogni occorrenza di InfoTelefono con any?
function function listaADizionario(lista: any[], generaId: (arg: any) => string): {
[k: string]: any;
}
listaADizionario(
lista: any[]lista: any[],
generaId: (arg: any) => stringgeneraId: (arg: anyarg: any) => string,
): { [k: stringk: string]: any } {
// niente cambia nel codice qui sotto
const const dizionario: {
[k: string]: any;
}
dizionario: { [k: stringk: string]: any } = {};
lista: any[]lista.Array<any>.forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): voidPerforms the specified action for each element in an array.forEach((elemento: anyelemento) => {
const const chiaveDizionario: stringchiaveDizionario = generaId: (arg: any) => stringgeneraId(elemento: anyelemento);
const dizionario: {
[k: string]: any;
}
dizionario[const chiaveDizionario: stringchiaveDizionario] = elemento: anyelemento;
});
return const dizionario: {
[k: string]: any;
}
dizionario;
}
const const dizionario: {
[k: string]: any;
}
dizionario = function listaADizionario(lista: any[], generaId: (arg: any) => string): {
[k: string]: any;
}
listaADizionario(
[{ nome: stringnome: "Mike" }, { nome: stringnome: "Mark" }],
(item: anyitem) => item: anyitem.nome,
);
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 dizionario: {
[k: string]: any;
}
dizionario);
// nessun errore di compilazione qui, ma è un disastro annunciato a runtime!
const dizionario: {
[k: string]: any;
}
dizionario.__type[string]: anyMike.non.dovrei.poter.fare.questo.NOOOOOOO;
Ok, questo funziona a runtime se lo provi nel Playground, ma ogni elemento nel nostro dizionario è un any. Diventando più flessibili, e cercando di gestire una varietà di elementi diversi, abbiamo di fatto perso tutte le informazioni di tipo utili che avevamo.
Quello che ci serve qui è un meccanismo per definire una relazione tra il tipo della cosa che ci viene passata, e il tipo della cosa che restituiremo. Questo è esattamente ciò di cui si occupano i Generics.
Definire un parametro di tipo
I parametri di tipo si possono pensare come "argomenti di funzione, ma per i tipi".
Le funzioni possono restituire valori diversi, a seconda degli argomenti che passi loro.
I generics possono cambiare il loro tipo, a seconda dei parametri di tipo che usi insieme a loro.
La firma della nostra funzione includerà ora un parametro di tipo T:
function listaADizionario<T>(
lista: T[],
generaId: (arg: T) => string,
): { [k: string]: T } {
const dizionario: { [k: string]: T } = {};
return dizionario;
}Vediamo cosa significa questo codice.
Il parametro di tipo, e il suo uso per fornire il tipo di un argomento
<T>a destra dilistaADizionario— significa che il tipo di questa funzione è ora parametrizzato in termini di un parametro di tipoT(che può cambiare a ogni singolo utilizzo).lista: T[]come primo argomento — significa che accettiamo una lista diT.- TypeScript inferirà cos'è
Tin base al singolo utilizzo, a seconda del tipo di array che passiamo. Se usiamo unostring[],Tsaràstring; se usiamo unnumber[],Tsarànumber.
- TypeScript inferirà cos'è
Prova a convincerti di queste prime due idee con questo esempio molto più semplice (e più inutile):
function avvolgiInArray<T>(arg: T): [T] {
return [arg];
}Nota come, nei tre esempi di avvolgiInArray qui sotto, il <T> che vediamo nella dichiarazione qui sopra viene sostituito da "il tipo della cosa che passiamo come argomento" — number, Date, e RegExp:
function function avvolgiInArray<T>(arg: T): [T]avvolgiInArray<function (type parameter) T in avvolgiInArray<T>(arg: T): [T]T>(arg: Targ: function (type parameter) T in avvolgiInArray<T>(arg: T): [T]T): [function (type parameter) T in avvolgiInArray<T>(arg: T): [T]T] {
return [arg: Targ];
}
function avvolgiInArray<number>(arg: number): [number]avvolgiInArray(3);
function avvolgiInArray<Date>(arg: Date): [Date]avvolgiInArray(new var Date: DateConstructor
new () => Date (+4 overloads)
Date());
function avvolgiInArray<RegExp>(arg: RegExp): [RegExp]avvolgiInArray(new var RegExp: RegExpConstructor
new (pattern: RegExp | string, flags?: string) => RegExp (+2 overloads)
RegExp("/s/"));
Ok, torniamo all'esempio più significativo della nostra funzione listaADizionario:
function listaADizionario<T>(
lista: T[],
generaId: (arg: T) => string, // anche questa usa T come argomento
): { [k: string]: T } {
const dizionario: { [k: string]: T } = {};
return dizionario;
}generaId: (arg: T) => string è una callback che anch'essa usa T come argomento. Questo significa che...
- Otteniamo i benefici del type checking, all'interno della funzione
generaId. - Otteniamo un certo allineamento nel type checking tra l'array e la funzione
generaId.
function function listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
listaADizionario<function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T>(
lista: T[]lista: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T[], // prende la lista come argomento
generaId: (arg: T) => stringgeneraId: (arg: Targ: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T) => string, // una callback per ottenere gli id
): { [k: stringk: string]: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T } {
const const dizionario: {
[k: string]: T;
}
dizionario: { [k: stringk: string]: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T } = {};
return const dizionario: {
[k: string]: T;
}
dizionario;
}
// TypeScript sa che T è Date, dedotto dal tipo dell'array!
function listaADizionario<Date>(lista: Date[], generaId: (arg: Date) => string): {
[k: string]: Date;
}
listaADizionario(
[
new var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date("10-01-2021"),
new var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date("03-14-2021"),
new var Date: DateConstructor
new (value: number | string | Date) => Date (+4 overloads)
Date("06-03-2021"),
],
(arg: Datearg) => arg: Datearg.Date.toISOString(): stringReturns a date as a string value in ISO format.toISOString(),
);Un'ultima cosa da esaminare: il tipo di ritorno. Nel modo in cui abbiamo definito questa funzione, un T[] verrà trasformato in { [k: string]: T } per qualsiasi T a nostra scelta.
Ora, mettiamo tutto insieme con l'esempio originale da cui siamo partiti:
interface InfoTelefono {
InfoTelefono.idCliente: stringidCliente: string;
InfoTelefono.prefisso: stringprefisso: string;
InfoTelefono.numero: stringnumero: string;
}
const const listaNumeri: {
idCliente: string;
prefisso: string;
numero: string;
}[]
listaNumeri = [
{ idCliente: stringidCliente: "0001", prefisso: stringprefisso: "321", numero: stringnumero: "123-4566" },
{ idCliente: stringidCliente: "0002", prefisso: stringprefisso: "174", numero: stringnumero: "142-3626" },
];
function function listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
listaADizionario<function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T>(
lista: T[]lista: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T[],
generaId: (arg: T) => stringgeneraId: (arg: Targ: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T) => string,
): { [k: stringk: string]: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T } {
const const dizionario: {
[k: string]: T;
}
dizionario: { [k: stringk: string]: function (type parameter) T in listaADizionario<T>(lista: T[], generaId: (arg: T) => string): {
[k: string]: T;
}
T } = {};
lista: T[]lista.Array<T>.forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): voidPerforms the specified action for each element in an array.forEach((elemento: Telemento) => {
const const chiaveDizionario: stringchiaveDizionario = generaId: (arg: T) => stringgeneraId(elemento: Telemento);
const dizionario: {
[k: string]: T;
}
dizionario[const chiaveDizionario: stringchiaveDizionario] = elemento: Telemento;
});
return const dizionario: {
[k: string]: T;
}
dizionario;
}
const const dizionario1: {
[k: string]: {
nome: string;
};
}
dizionario1 = function listaADizionario<{
nome: string;
}>(lista: {
nome: string;
}[], generaId: (arg: {
nome: string;
}) => string): {
[k: string]: {
nome: string;
};
}
listaADizionario([{ nome: stringnome: "Mike" }, { nome: stringnome: "Mark" }], (item: {
nome: string;
}
item) => item: {
nome: string;
}
item.nome: stringnome);
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 dizionario1: {
[k: string]: {
nome: string;
};
}
dizionario1);
const dizionario1: {
[k: string]: {
nome: string;
};
}
dizionario1.{
nome: string;
}
Mike;
const const dizionario2: {
[k: string]: {
idCliente: string;
prefisso: string;
numero: string;
};
}
dizionario2 = function listaADizionario<{
idCliente: string;
prefisso: string;
numero: string;
}>(lista: {
idCliente: string;
prefisso: string;
numero: string;
}[], generaId: (arg: {
idCliente: string;
prefisso: string;
numero: string;
}) => string): {
[k: string]: {
idCliente: string;
prefisso: string;
numero: string;
};
}
listaADizionario(const listaNumeri: {
idCliente: string;
prefisso: string;
numero: string;
}[]
listaNumeri, (p: {
idCliente: string;
prefisso: string;
numero: string;
}
p) => p: {
idCliente: string;
prefisso: string;
numero: string;
}
p.idCliente: stringidCliente);
const dizionario2: {
[k: string]: {
idCliente: string;
prefisso: string;
numero: string;
};
}
dizionario2.{
idCliente: string;
prefisso: string;
numero: string;
}
fax;
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 dizionario2: {
[k: string]: {
idCliente: string;
prefisso: string;
numero: string;
};
}
dizionario2);Osserviamo attentamente e assicuriamoci di aver capito cosa sta succedendo:
- Esegui questo nel TypeScript Playground, e verifica di vedere i log che ti aspetti.
- Guarda da vicino i tipi degli elementi in
dizionario1edizionario2sopra, per convincerti che otteniamo un tipo di dizionario diverso dalistaADizionario, a seconda del tipo dell'array che passiamo.
Questo è molto meglio del nostro "dizionario di any", perché non perdiamo alcuna informazione di tipo come effetto collaterale della trasformazione da lista a dizionario. È esattamente come il nostro archivista: qualunque cosa ci sia dentro gli scatoloni, sappiamo sempre cosa aspettarci quando li riapriamo.
Best practice
- Usa ogni parametro di tipo almeno due volte. Se lo usi di meno, probabilmente stai facendo un cast con la keyword
as. Guardiamo questo esempio:
function function restituisciCome<T>(arg: any): TrestituisciCome<function (type parameter) T in restituisciCome<T>(arg: any): TT>(arg: anyarg: any): function (type parameter) T in restituisciCome<T>(arg: any): TT {
return arg: anyarg; // 🚨 un `any` che *sembrerà* un `T`
}
// 🚨 PERICOLO! 🚨 (ma window non è affatto un numero!)
const const primo: numberprimo = function restituisciCome<number>(arg: any): numberrestituisciCome<number>(var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window);
const const stessaCosa: numberstessaCosa = var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window as any as number;In questo esempio, abbiamo detto una bugia a TypeScript, affermando che window è un number (ma non lo è...). Ora, TypeScript non riuscirà a intercettare errori che dovrebbe intercettare, perché gli abbiamo tolto di mano l'unico strumento che aveva per verificare la nostra affermazione: usare T una sola volta, solo nel tipo di ritorno, significa che il compilatore non ha nessun altro punto del codice con cui "confrontare" quel T per validarlo.
