Una funzione TypeScript è una funzione JavaScript con i tipi sui parametri e, se vuoi, sul valore di ritorno. Ogni parametro riceve un'annotazione, il tipo di ritorno va dopo la lista dei parametri, e il compilatore controlla ogni chiamata rispetto a entrambi.
Entrambe le chiamate sbagliate sono errori di compilazione (TS2345 e TS2554). I commenti // @ts-expect-error dicono al compilatore che sulla riga successiva è atteso un errore, così il resto del file viene comunque eseguito. Togline uno ed esegui di nuovo per vedere il messaggio vero.
Tipi dei parametri
Ogni parametro si scrive name: Type. Con strict, attivo di default in TypeScript 7, un parametro senza annotazione e senza un contesto da cui dedurre il tipo è un errore: Parameter 'x' implicitly has an 'any' type. (TS7006). In pratica, quindi, ogni parametro di una funzione a sé viene annotato.
Come tipo di parametro va bene qualsiasi tipo: primitivi, array, tipi oggetto, unioni, altre funzioni.
TypeScript controlla anche il numero di argomenti. Passarne più o meno di quanti la funzione ne dichiara è un errore, a differenza del JavaScript puro, che riempie quelli mancanti con undefined e ignora quelli in più. Per rendere un parametro facoltativo o dargli un valore di default, vedi parametri facoltativi.
Tipi di ritorno: annotare o dedurre
Il tipo di ritorno segue la parentesi di chiusura: function f(): Type. È facoltativo. Senza, TypeScript deduce il tipo da ogni return nel corpo.
Un'annotazione si guadagna il posto quando la funzione è esportata, quando ha più percorsi di ritorno o quando è ricorsiva. L'errore compare allora dentro la funzione, sul return sbagliato, invece che in una chiamata. Un caso classico è un ramo che si dimentica di restituire un valore:
Il compilatore segnala index.ts(2,32): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. Aggiungi un return "C"; finale e il codice viene eseguito. Senza l'annotazione : string la funzione compilerebbe con il tipo dedotto "A" | "B" | undefined, e il problema si sposterebbe su chi usa il risultato.
void: funzioni che non restituiscono nulla
Una funzione che si limita a fare qualcosa (stampare, scrivere, modificare dati) ha tipo di ritorno void. È ciò che TypeScript deduce quando non c'è un return con un valore, e puoi scriverlo in modo esplicito.
Queste regole su void sorprendono spesso:
| Situazione | Consentito? |
|---|---|
function f(): void { return 42; } | No: Type 'number' is not assignable to type 'void'. (TS2322) |
const f: () => void = () => 42; | Sì: il valore viene restituito ma chi chiama non deve contarci |
function f(): undefined {} | Sì (da TypeScript 5.1 in poi) |
Usare il risultato di una funzione void in un if | No: An expression of type 'void' cannot be tested for truthiness. (TS1345) |
La seconda riga è voluta. È il motivo per cui arr.forEach(x => list.push(x)) compila anche se push restituisce un numero. void in un tipo funzione significa "qualunque cosa restituisca, nessuno la legge". Per una funzione che non ritorna mai (lancia sempre un'eccezione o cicla all'infinito), il tipo di ritorno è never.
Arrow function e function expression
Arrow function e function expression accettano le stesse annotazioni. Il tipo di ritorno va dopo la lista dei parametri, prima di =>.
Senza le parentesi, (x, y) => { x, y } è un blocco senza return, e la funzione restituisce undefined. Le regole a runtime delle arrow function (nessun this proprio, nessun arguments) sono le stesse di JavaScript; vedi arrow function in JavaScript.
Quando una funzione è scritta direttamente come argomento, di solito non ne annoti affatto i parametri. TypeScript sa cosa riceve la callback dalla funzione a cui viene passata (contextual typing): in [1, 2].map(n => n * 2), n è già number. Come scrivere il tipo di una funzione come valore, (n: number) => string, è spiegato in tipi funzione.
Restituire più valori
Una funzione restituisce un solo valore, quindi restituisci un oggetto o una tupla e destrutturalo.
Il tipo di ritorno tupla ha bisogno dell'annotazione. Senza, [a, b] viene dedotto come string[]: la destrutturazione funziona comunque, ma il tipo non dice più che gli elementi sono esattamente due.
Funzioni async
Una funzione async restituisce sempre una Promise. Annota il tipo di ritorno come Promise<T>, dove T è ciò che la funzione restituisce al suo interno.
async function loadScore(id: number): Promise<number> {
await new Promise((resolve) => setTimeout(resolve, 10));
return id * 10;
}
async function main(): Promise<void> {
const score = await loadScore(4); // score: number
console.log("score", score);
}
main();
Scrivere async function f(): number è un errore: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'? (TS1064).
Il parametro this
JavaScript decide this al momento della chiamata. TypeScript ti permette di dichiarare cosa deve essere this con un finto primo parametro chiamato this. Viene rimosso dal JavaScript compilato e chi chiama non lo passa.
interface Counter {
count: number;
}
function increment(this: Counter, by: number): void {
this.count += by;
}
const c = { count: 0, increment };
c.increment(2); // fine: this is c
increment(2);
// error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Counter'.
Senza il parametro this, this dentro una funzione a sé è un any implicito e la modalità strict segnala 'this' implicitly has type 'any' because it does not have a type annotation. (TS2683). Nei metodi di una classe this ha già il tipo dell'istanza, quindi il parametro serve soprattutto per funzioni a sé collegate a oggetti e per callback che le librerie chiamano con un this specifico.
Domande frequenti
Come si specifica il tipo di ritorno di una funzione in TypeScript?
Scrivi i due punti e il tipo dopo la lista dei parametri: function total(a: number, b: number): number { ... }. Per una arrow function va nello stesso punto: const total = (a: number, b: number): number => a + b;. Se lo ometti, TypeScript deduce il tipo di ritorno dalle istruzioni return.
Devo annotare sempre il tipo di ritorno in TypeScript?
È facoltativo, perché il tipo di ritorno viene dedotto. Annotalo nelle funzioni esportate e in quelle con più percorsi di return: l'annotazione documenta il contratto, e un return sbagliato viene segnalato dentro la funzione invece che in qualche chiamata lontana.
Che differenza c'è tra void e undefined in TypeScript?
void significa "chi chiama non deve usare il valore di ritorno". undefined è un tipo di valore concreto. Una funzione dichiarata (): void non può restituire un valore, ma un tipo callback () => void accetta una funzione che restituisce qualcosa, e il risultato viene semplicemente ignorato. Usa void per le funzioni che non restituiscono nulla e undefined solo quando chi chiama confronta davvero il risultato con undefined.
Come si restituiscono più valori da una funzione TypeScript?
Restituisci un oggetto ({ min: number; max: number }) o una tupla ([number, number]) e destrutturalo dove chiami la funzione. Gli oggetti sono più chiari quando i valori hanno significati diversi; le tuple si leggono bene per coppie brevi come [value, setValue].
Qual è il tipo di ritorno di una funzione async in TypeScript?
Sempre una Promise. Una funzione async che restituisce un number ha tipo di ritorno Promise<number>, e scriverci : number è un errore di compilazione (TS1064). Una funzione che non restituisce nulla è Promise<void>.