Tipi callable e costruibili
Tipi callable e costruibili
Pensa a un distributore automatico di bevande. Ha un'interfaccia molto precisa: accetta monete di certi tagli, un pulsante per ogni prodotto, ed eroga una lattina in cambio. Non importa che marca di distributore sia, o quale azienda l'abbia costruito: se rispetta quell'interfaccia (accetta quelle monete, ha quei pulsanti, eroga quel prodotto), tu sai esattamente come interagirci. Un "tipo callable" in TypeScript è esattamente questo: la descrizione di come si "interagisce" con una funzione — quali "monete" (argomenti) accetta, e cosa "eroga" (il valore di ritorno) — indipendentemente da come è stata implementata internamente.
Finora abbiamo trattato tipi di argomenti e di ritorno delle funzioni, ma ci sono alcuni concetti più approfonditi che dobbiamo affrontare, tra cui le "firme multiple" (overload) e i tipi callable.
Tipi callable
Sia i type alias che le interfacce offrono la possibilità di descrivere call signature (firme chiamabili):
interface CalcoloDueNumeri {
(x: numberx: number, y: numbery: number): number;
}
type type CalcoloDueNumeriAlias = (x: number, y: number) => numberCalcoloDueNumeriAlias = (x: numberx: number, y: numbery: number) => number;
const const somma: CalcoloDueNumerisomma: CalcoloDueNumeri = (a: numbera, b: numberb) => a: numbera + b: numberb;
const const sottrai: CalcoloDueNumeriAliassottrai: type CalcoloDueNumeriAlias = (x: number, y: number) => numberCalcoloDueNumeriAlias = (x: numberx, y: numbery) => x: numberx - y: numbery;
Fermiamoci un attimo a notare:
- Il tipo di ritorno per un'interfaccia è
: number, mentre per il type alias è=> number. - Dato che forniamo i tipi per le funzioni
sommaesottraitramite l'annotazione sulla costante, non serve fornire annotazioni di tipo per la lista degli argomenti o per il valore di ritorno di ciascuna singola funzione: TypeScript le deduce dal contesto.
void1
A volte le funzioni non restituiscono nulla, e sappiamo per esperienza con JavaScript che quello che succede davvero nella situazione qui sotto è che x sarà undefined:
function function stampaJSONFormattato(obj: string[]): voidstampaJSONFormattato(obj: string[]obj: string[]) {
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(var JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.JSON.JSON.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.stringify(obj: string[]obj, null, " "));
}
const const x: voidx = function stampaJSONFormattato(obj: string[]): voidstampaJSONFormattato(["ciao", "mondo"]);
Perché compare come void, allora?
void è un tipo speciale, usato specificamente per descrivere i valori di ritorno delle funzioni. Ha il seguente significato:
Il valore di ritorno di una funzione che restituisce
voidè pensato per essere ignorato.
Potremmo tipizzare le funzioni come se restituissero undefined, ma ci sono alcune differenze interessanti che spiegano il motivo dell'esistenza di void:
function function invocaTraQuattroSecondi(callback: () => undefined): voidinvocaTraQuattroSecondi(callback: () => undefinedcallback: () => undefined) {
function setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+2 overloads)Schedules execution of a one-time `callback` after `delay` milliseconds.
The `callback` will likely not be invoked in precisely `delay` milliseconds.
Node.js makes no guarantees about the exact timing of when callbacks will fire,
nor of their ordering. The callback will be called as close as possible to the
time specified.
When `delay` is larger than `2147483647` or less than `1` or `NaN`, the `delay`
will be set to `1`. Non-integer delays are truncated to an integer.
If `callback` is not a function, a `TypeError` will be thrown.
This method has a custom variant for promises that is available using
`timersPromises.setTimeout()`.setTimeout(callback: () => undefinedcallback, 4000);
}
function function invocaTraCinqueSecondi(callback: () => void): voidinvocaTraCinqueSecondi(callback: () => voidcallback: () => void) {
function setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+2 overloads)Schedules execution of a one-time `callback` after `delay` milliseconds.
The `callback` will likely not be invoked in precisely `delay` milliseconds.
Node.js makes no guarantees about the exact timing of when callbacks will fire,
nor of their ordering. The callback will be called as close as possible to the
time specified.
When `delay` is larger than `2147483647` or less than `1` or `NaN`, the `delay`
will be set to `1`. Non-integer delays are truncated to an integer.
If `callback` is not a function, a `TypeError` will be thrown.
This method has a custom variant for promises that is available using
`timersPromises.setTimeout()`.setTimeout(callback: () => voidcallback, 5000);
}
const const valori: number[]valori: number[] = [];
function invocaTraQuattroSecondi(callback: () => undefined): voidinvocaTraQuattroSecondi(() => valori.push(4));function invocaTraCinqueSecondi(callback: () => void): voidinvocaTraCinqueSecondi(() => const valori: number[]valori.Array<number>.push(...items: number[]): numberAppends new elements to the end of an array, and returns the new length of the array.push(4)); // questa invece va beneSuccede che Array.prototype.push restituisce un numero, e la nostra funzione invocaTraQuattroSecondi qui sopra non è contenta di ricevere questo valore restituito dalla callback — mentre invocaTraCinqueSecondi, dichiarando void, accetta serenamente qualsiasi cosa venga restituita, perché ha dichiarato fin da subito "non mi interessa cosa restituisci".
Construct signature
Le construct signature (firme di costruzione) sono simili alle call signature, tranne per il fatto che descrivono cosa dovrebbe succedere quando la keyword new viene usata in uno scenario di istanziazione.
interface CostruttoreDiData {
new (valore: numbervalore: number): Date;
}
let let MioCostruttoreDiData: CostruttoreDiDataMioCostruttoreDiData: CostruttoreDiData = var Date: DateConstructorEnables basic storage and retrieval of dates and times.Date;
const const d: Dated = new let MioCostruttoreDiData: CostruttoreDiData
new (valore: number) => Date
MioCostruttoreDiData(1697923072611);
Sono rare, ma se mai dovessi incontrarle, ora sai cosa sono.
Overload di funzione
Immagina la seguente situazione:
<iframe src="https://example.com" />
<form>
<input type="text" name="nome" />
<input type="text" name="email" />
<input type="password" name="password" />
<input type="submit" value="Login" />
</form>Cosa succederebbe se dovessimo creare una funzione che ci permette di registrare un "listener principale per gli eventi"?
- Se ci viene passato un elemento
form, dovremmo permettere la registrazione di una "callback di submit". - Se ci viene passato un elemento
iframe, dovremmo permettere la registrazione di una "callback per ipostMessage".
Proviamoci:
type type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm = (data: FormDatadata: FormData) => void;
type type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio = (evt: MessageEvent<any>evt: interface MessageEvent<T = any>The **`MessageEvent`** interface represents a message received by a target object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent)MessageEvent) => void;
function function gestisciEventoPrincipale(elem: HTMLFormElement | HTMLIFrameElement, handler: GestoreSubmitForm | GestoreMessaggio): voidgestisciEventoPrincipale(
elem: HTMLFormElement | HTMLIFrameElementelem: HTMLFormElement | HTMLIFrameElement,
handler: GestoreSubmitForm | GestoreMessaggiohandler: type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm | type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio,
) {}
const const mioFrame: HTMLIFrameElementmioFrame = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementsByTagName<"iframe">(qualifiedName: "iframe"): HTMLCollectionOf<HTMLIFrameElement> (+4 overloads)The **`getElementsByTagName`** method of The complete document is searched, including the root node.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByTagName)getElementsByTagName("iframe")[0];
function gestisciEventoPrincipale(elem: HTMLFormElement | HTMLIFrameElement, handler: GestoreSubmitForm | GestoreMessaggio): voidgestisciEventoPrincipale(const mioFrame: HTMLIFrameElementmioFrame, (val) => {});Questo non va bene, e in effetti TypeScript se ne accorge subito: dato che handler può essere un GestoreSubmitForm o un GestoreMessaggio, e i due tipi di funzione non condividono una firma comune, il compilatore non riesce nemmeno a inferire il tipo di val nella callback — da qui l'errore. Ma il problema di fondo è più ampio: stiamo permettendo troppe possibilità, incluse cose che non vogliamo supportare (per esempio, usare un HTMLIFrameElement con GestoreSubmitForm, il che non ha molto senso).
Possiamo risolvere questo con gli overload di funzione, dove definiamo più "intestazioni" di funzione che fungono da punti di ingresso verso un'unica implementazione:
type type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm = (data: FormDatadata: FormData) => void;
type type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio = (evt: MessageEvent<any>evt: interface MessageEvent<T = any>The **`MessageEvent`** interface represents a message received by a target object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent)MessageEvent) => void;
function function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale(
elem: HTMLFormElementelem: HTMLFormElement,
handler: GestoreSubmitFormhandler: type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm,
): void;
function function gestisciEventoPrincipale(elem: HTMLIFrameElement, handler: GestoreMessaggio): void (+1 overload)gestisciEventoPrincipale(
elem: HTMLIFrameElementelem: HTMLIFrameElement,
handler: GestoreMessaggiohandler: type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio,
): void;
function function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale(
elem: HTMLFormElement | HTMLIFrameElementelem: HTMLFormElement | HTMLIFrameElement,
handler: GestoreSubmitForm | GestoreMessaggiohandler: type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm | type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio,
) {}
const const mioFrame: HTMLIFrameElementmioFrame = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementsByTagName<"iframe">(qualifiedName: "iframe"): HTMLCollectionOf<HTMLIFrameElement> (+4 overloads)The **`getElementsByTagName`** method of The complete document is searched, including the root node.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByTagName)getElementsByTagName("iframe")[0];
const const mioForm: HTMLFormElementmioForm = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementsByTagName<"form">(qualifiedName: "form"): HTMLCollectionOf<HTMLFormElement> (+4 overloads)The **`getElementsByTagName`** method of The complete document is searched, including the root node.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByTagName)getElementsByTagName("form")[0];
function gestisciEventoPrincipale(elem: HTMLIFrameElement, handler: GestoreMessaggio): void (+1 overload)gestisciEventoPrincipale(const mioFrame: HTMLIFrameElementmioFrame, (val: MessageEvent<any>val) => {
val: MessageEvent<any>val;
});
function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale(const mioForm: HTMLFormElementmioForm, (val: FormDataval) => {
val: FormDataval;
});Guarda un po'! Abbiamo effettivamente creato un collegamento tra il primo e il secondo argomento, che permette al tipo dell'argomento della nostra callback di cambiare, in base al tipo del primo argomento di gestisciEventoPrincipale.
Diamo un'occhiata più da vicino alla dichiarazione della funzione:
function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale;
Sembrano tre dichiarazioni di funzione, ma in realtà sono due "intestazioni" che definiscono una lista di argomenti e un tipo di ritorno, seguite dalla nostra implementazione originale.
Se guardi con attenzione i tooltip e i suggerimenti di autocompletamento che ottieni dal language server di TypeScript, è chiaro che puoi chiamare solo le due "intestazioni" esposte, lasciando la "terza intestazione + implementazione" sottostante inaccessibile dall'esterno.
Un'ultima cosa importante da notare: la firma della funzione "implementazione" deve essere abbastanza generale da includere tutto ciò che è possibile attraverso la prima e la seconda intestazione esposta. Per esempio, questo non funzionerebbe:
type type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm = (data: FormDatadata: FormData) => void;
type type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio = (evt: MessageEvent<any>evt: interface MessageEvent<T = any>The **`MessageEvent`** interface represents a message received by a target object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent)MessageEvent) => void;
function function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale(
elem: HTMLFormElementelem: HTMLFormElement,
handler: GestoreSubmitFormhandler: type GestoreSubmitForm = (data: FormData) => voidGestoreSubmitForm,
): void;
function gestisciEventoPrincipale( elem: HTMLIFrameElementelem: HTMLIFrameElement,
handler: GestoreMessaggiohandler: type GestoreMessaggio = (evt: MessageEvent) => voidGestoreMessaggio,
): void;
function function gestisciEventoPrincipale(elem: HTMLFormElement, handler: GestoreSubmitForm): void (+1 overload)gestisciEventoPrincipale(elem: HTMLFormElementelem: HTMLFormElement) {}Tipi this
A volte abbiamo una funzione "libera" (non legata a un oggetto) che ha un'idea molto precisa di cosa dovrebbe essere this, nel momento in cui viene invocata.
Per esempio, se avessimo un event listener del DOM per un bottone:
<button onclick="mioGestoreClick">Clicca qui!</button>Potremmo definire mioGestoreClick così:
function function mioGestoreClick(event: Event): voidmioGestoreClick(event: Eventevent: Event) {
this.disabled = true;}Con l'opzione compilerOptions.noImplicitThis attiva (è inclusa in strict: true, che dovresti sempre usare), TypeScript rifiuta di lasciare che this sia un any implicito, e ci obbliga a dichiararne esplicitamente il tipo.
Per risolvere il problema, dobbiamo dare a questa funzione un tipo per this:
function function mioGestoreClick(this: HTMLButtonElement, event: Event): voidmioGestoreClick(this: HTMLButtonElementthis: HTMLButtonElement, event: Eventevent: Event) {
this.HTMLButtonElement.disabled: booleanThe **`HTMLButtonElement.disabled`** property indicates whether the control is disabled, meaning that it does not accept any clicks.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLButtonElement/disabled)disabled = true;
}
mioGestoreClick(new Event("click"));Ora, quando proviamo a invocare direttamente mioGestoreClick sull'ultima riga dello snippet qui sopra, otteniamo un nuovo errore del compilatore. In pratica, non abbiamo fornito il this che questa funzione dichiara di volere.
function function mioGestoreClick(this: HTMLButtonElement, event: Event): voidmioGestoreClick(this: HTMLButtonElementthis: HTMLButtonElement, event: Eventevent: Event) {
this.HTMLButtonElement.disabled: booleanThe **`HTMLButtonElement.disabled`** property indicates whether the control is disabled, meaning that it does not accept any clicks.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLButtonElement/disabled)disabled = true;
}
const const mioBottone: HTMLButtonElementmioBottone = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementsByTagName<"button">(qualifiedName: "button"): HTMLCollectionOf<HTMLButtonElement> (+4 overloads)The **`getElementsByTagName`** method of The complete document is searched, including the root node.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByTagName)getElementsByTagName("button")[0];
const const handlerAgganciato: (event: Event) => voidhandlerAgganciato = function mioGestoreClick(this: HTMLButtonElement, event: Event): voidmioGestoreClick.CallableFunction.bind<(this: HTMLButtonElement, event: Event) => void>(this: (this: HTMLButtonElement, event: Event) => void, thisArg: HTMLButtonElement): (event: Event) => void (+1 overload)For a given function, creates a bound function that has the same body as the original function.
The this object of the bound function is associated with the specified object, and has the specified initial parameters.bind(const mioBottone: HTMLButtonElementmioBottone);
const handlerAgganciato: (event: Event) => voidhandlerAgganciato(new var Event: new (type: string, eventInitDict?: EventInit) => EventThe **`Event`** interface represents an event which takes place on an `EventTarget`.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Event)Event("click")); // versione agganciata: ok
function mioGestoreClick(this: HTMLButtonElement, event: Event): voidmioGestoreClick.CallableFunction.call<HTMLButtonElement, [Event], void>(this: (this: HTMLButtonElement, args_0: Event) => void, thisArg: HTMLButtonElement, args_0: Event): voidCalls the function with the specified object as the this value and the specified rest arguments as the arguments.call(const mioBottone: HTMLButtonElementmioBottone, new var Event: new (type: string, eventInitDict?: EventInit) => EventThe **`Event`** interface represents an event which takes place on an `EventTarget`.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Event)Event("click")); // ok anche questoNota che TypeScript capisce che .bind, .call o .apply faranno sì che il this corretto venga passato alla funzione come parte della sua invocazione.
Best practice per i tipi delle funzioni
Dichiara esplicitamente i tipi di ritorno
TypeScript è in grado di inferire i tipi di ritorno delle funzioni in modo piuttosto efficace, ma questo comportamento "accomodante" può portare a effetti a catena non intenzionali, dove i tipi cambiano in punti imprevisti del tuo codebase.
Considera questo esempio:
type type JSONPrimitive = string | number | boolean | nullJSONPrimitive = string | number | boolean | null;
type type JSONObject = {
[k: string]: JSONValue;
}
JSONObject = { [k: stringk: string]: type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue };
type type JSONArray = JSONValue[]JSONArray = type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue[];
type type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue = type JSONArray = JSONValue[]JSONArray | type JSONObject = {
[k: string]: JSONValue;
}
JSONObject | type JSONPrimitive = string | number | boolean | nullJSONPrimitive;
export async function function recuperaDati(url: string): Promise<{
proprieta: string[];
}>
recuperaDati(url: stringurl: string) {
const const risposta: Responserisposta = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch(url: stringurl);
const const dati: {
proprieta: string[];
}
dati = (await const risposta: Responserisposta.Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json()) as {
proprieta: string[]proprieta: string[];
};
return const dati: {
proprieta: string[];
}
dati;
}
function function caricaDati(): voidcaricaDati() {
function recuperaDati(url: string): Promise<{
proprieta: string[];
}>
recuperaDati("https://example.com").Promise<{ proprieta: string[]; }>.then<void, never>(onfulfilled?: ((value: {
proprieta: string[];
}) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
Attaches callbacks for the resolution and/or rejection of the Promise.then((risultato: {
proprieta: string[];
}
risultato) => {
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(risultato: {
proprieta: string[];
}
risultato.proprieta: string[]proprieta.Array<string>.join(separator?: string): stringAdds all the elements of an array into a string, separated by the specified separator string.join(", "));
});
}e se facessimo una modifica apparentemente innocente?
export async function recuperaDati(url: string) {
const risposta = await fetch(url)
+ if (risposta.ok) {
const dati = await risposta.json()
return dati
+ }
}Vedremo comparire alcuni errori di type checking, ma nel punto in cui la funzione viene invocata, non dove è dichiarata.
Immagina se stessimo passando questo valore attraverso diverse altre funzioni prima di arrivare al punto in cui il type checking ci avvisa di un problema!
type type JSONPrimitive = string | number | boolean | nullJSONPrimitive = string | number | boolean | null;
type type JSONObject = {
[k: string]: JSONValue;
}
JSONObject = { [k: stringk: string]: type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue };
type type JSONArray = JSONValue[]JSONArray = type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue[];
type type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue = type JSONArray = JSONValue[]JSONArray | type JSONObject = {
[k: string]: JSONValue;
}
JSONObject | type JSONPrimitive = string | number | boolean | nullJSONPrimitive;
async function function recuperaDati(url: string): Promise<{
proprieta: string[];
} | undefined>
recuperaDati(url: stringurl: string) {
const const risposta: Responserisposta = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch(url: stringurl);
if (const risposta: Responserisposta.Response.ok: booleanThe **`ok`** read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok)ok) {
const const dati: {
proprieta: string[];
}
dati = (await const risposta: Responserisposta.Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json()) as {
proprieta: string[]proprieta: string[];
};
return const dati: {
proprieta: string[];
}
dati;
}
// implicitamente, se risposta.ok è false, non restituiamo nulla!
}
function function caricaDati(): voidcaricaDati() {
function recuperaDati(url: string): Promise<{
proprieta: string[];
} | undefined>
recuperaDati("https://example.com").Promise<{ proprieta: string[]; } | undefined>.then<void, never>(onfulfilled?: ((value: {
proprieta: string[];
} | undefined) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
Attaches callbacks for the resolution and/or rejection of the Promise.then((risultato: {
proprieta: string[];
} | undefined
risultato) => {
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(risultato.proprieta: string[]proprieta.Array<string>.join(separator?: string): stringAdds all the elements of an array into a string, separated by the specified separator string.join(", ")); });
}Se usiamo lo stesso esempio, ma definiamo esplicitamente un tipo di ritorno, il messaggio di errore emerge nel punto di dichiarazione:
type type JSONPrimitive = string | number | boolean | nullJSONPrimitive = string | number | boolean | null;
type type JSONObject = {
[k: string]: JSONValue;
}
JSONObject = { [k: stringk: string]: type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue };
type type JSONArray = JSONValue[]JSONArray = type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue[];
type type JSONValue = JSONPrimitive | JSONObject | JSONArrayJSONValue = type JSONArray = JSONValue[]JSONArray | type JSONObject = {
[k: string]: JSONValue;
}
JSONObject | type JSONPrimitive = string | number | boolean | nullJSONPrimitive;
async function function recuperaDati(url: string): Promise<{
proprieta: string[];
}>
recuperaDati(url: stringurl: string): Promise<{ proprieta: string[] }> { const const risposta: Responserisposta = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch(url: stringurl);
if (const risposta: Responserisposta.Response.ok: booleanThe **`ok`** read-only property of the Response interface contains a Boolean stating whether the response was successful (status in the range 200-299) or not.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok)ok) {
const const dati: {
proprieta: string[];
}
dati = (await const risposta: Responserisposta.Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json()) as {
proprieta: string[]proprieta: string[];
};
return const dati: {
proprieta: string[];
}
dati;
}
}
function function caricaDati(): voidcaricaDati() {
function recuperaDati(url: string): Promise<{
proprieta: string[];
}>
recuperaDati("https://example.com").Promise<{ proprieta: string[]; }>.then<void, never>(onfulfilled?: ((value: {
proprieta: string[];
}) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<void>
Attaches callbacks for the resolution and/or rejection of the Promise.then((risultato: {
proprieta: string[];
}
risultato) => {
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(risultato: {
proprieta: string[];
}
risultato.proprieta: string[]proprieta.Array<string>.join(separator?: string): stringAdds all the elements of an array into a string, separated by the specified separator string.join(", "));
});
}Dichiarare esplicitamente il tipo di ritorno ci costringe a gestire subito il "percorso mancante" (il caso in cui risposta.ok è false), invece di scoprirlo tre funzioni più in là, magari in un punto del codice scritto da qualcun altro che non ha idea di come sia fatta recuperaDati internamente.
Footnotes
-
Esiste un concetto nativo di keyword
voidin JavaScript, ma non è collegato al concetto TypeScript con lo stesso nome. ↩
