In TypeScript possiamo specificare con precisione quali valori una funzione può ricevere e quale valore può restituire. Questo permette di individuare errori già durante la compilazione e di ottenere un autocompletamento più preciso nell'editor.
Possiamo tipizzare:
Per specificare il tipo di un parametro utilizziamo la stessa sintassi delle definizioni esplicite del tipo:
function saluta(nome: string) {
console.log(`Ciao ${nome}`);
}In questo caso nome deve essere una string.
Possiamo quindi chiamare la funzione passando una stringa:
saluta("Andrea");Se invece utilizziamo un numero, come saluta(30), TypeScript segnalerà un errore perché il parametro richiede una string.
Possiamo tipizzare più parametri indicando il tipo di ciascuno:
function somma(a: number, b: number) {
return a + b;
}In questo caso entrambi i parametri devono essere number.
Possiamo specificare anche il tipo del valore restituito dalla funzione.
La sintassi prevede di inserire : dopo le parentesi dei parametri:
function somma(a: number, b: number): number {
return a + b;
}La parte : number indica che la funzione deve restituire un number. Ora possiamo chiamare la funzione così:
const risultato = somma(10, 20);Se proviamo a restituire una stringa, TypeScript segnala un errore perché il valore restituito non corrisponde al tipo dichiarato:
function somma(a: number, b: number): number {
return "30"; // Errore
}Non è sempre necessario specificare esplicitamente il tipo restituito. TypeScript può infatti dedurre il tipo di valore anche per il return delle funzioni.
Ad esempio, possiamo scrivere:
function somma(a: number, b: number) {
return a + b;
}TypeScript vede che a e b sono numeri e che la funzione restituisce il risultato della loro somma. Di conseguenza, deduce automaticamente che il valore restituito è un number.
Possiamo quindi scrivere:
const risultato = somma(10, 20);Se TypeScript è in grado di dedurre correttamente il tipo, spesso non è necessario dichiararlo manualmente.
La definizione esplicita del tipo del valore restituito può però essere utile quando vogliamo chiarire l'output previsto della funzione ed evitare la restituzione di tipi errati. Per esempio:
function creaMessaggio(nome: string): string {
return `Ciao ${nome}`;
}In questo modo TypeScript controllerà che ogni possibile percorso della funzione restituisca una string.
Una funzione può anche non restituire alcun valore. In questi casi TypeScript utilizza il tipo void.
Per esempio:
function mostraMessaggio(messaggio: string): void {
console.log(messaggio);
}La funzione esegue un'operazione, ma non restituisce un valore attraverso return.
In molti casi anche void può essere non essere dichiarato esplicitamente, perché TypeScript può dedurre automaticamente il tipo di ritorno appropriato:
function mostraMessaggio(messaggio: string) {
console.log(messaggio);
}Come per le tuple e gli oggetti, possiamo rendere un parametro opzionale utilizzando ?. Per esempio:
function saluta(nome: string, cognome?: string) {
if (cognome) {
console.log(`Ciao ${nome} ${cognome}`);
} else {
console.log(`Ciao ${nome}`);
}
}Ora possiamo chiamare la funzione in entrambi i modi:
saluta("Andrea");
saluta("Andrea", "Rossi");Il parametro cognome può quindi essere presente oppure assente.
Un parametro opzionale viene considerato da TypeScript come un valore che potrebbe non essere disponibile. In pratica, il suo tipo viene trattato come string | undefined.
I parametri opzionali devono trovarsi dopo i parametri obbligatori. Ad esempio, questa funzione è valida:
function saluta(nome: string, cognome?: string) {
// ...
}Non sarebbe invece corretto rendere opzionale il primo parametro lasciando obbligatorio il secondo:
function saluta(nome?: string, cognome: string) {
// ...
}Nell'ultimo esempio, non sarebbe possibile stabilire chiaramente quale argomento corrisponde a quale parametro quando ne viene passato soltanto uno: per questo motivo, TypeScript genera un errore.
Un parametro può avere un valore di default. Ad esempio, possiamo scrivere:
function saluta(nome: string, lingua = "italiano") {
console.log(`Ciao ${nome}, lingua: ${lingua}`);
}Se non specifichiamo lingua, viene utilizzato "italiano":
saluta("Andrea"); // "Ciao Andrea, lingua: italiano"
saluta("Andrea", "inglese"); // "Ciao Andrea, lingua: inglese"Quando non sappiamo quanti argomenti verranno passati a una funzione, possiamo utilizzare i parametri rest.
La sintassi è: ...nome:tipo[]. Ad esempio:
function somma(...numeri: number[]): number {
return numeri.reduce((totale, numero) => totale + numero, 0);
}Ora possiamo passare un numero qualsiasi di valori:
somma(10, 20); // 30
somma(10, 20, 30, 40); // 100Il parametro numeri viene trattato come un array di number. Di conseguenza, TypeScript controlla anche gli argomenti passati alla funzione. Ad esempio, somma(10, "20", 30); genererebbe un errore, perché "20" è una string.
Possiamo utilizzare un parametro rest insieme a normali parametri:
function somma(primo: number, ...numeri: number[]): number {
return primo + numeri.reduce((totale, numero) => totale + numero, 0);
}
somma(10, 20, 30, 40);Il parametro primo riceverà 10, mentre tutti gli altri valori finiranno nell'array numeri.
Il parametro rest deve essere necessariamente l'ultimo parametro della funzione.
In TypeScript possiamo anche descrivere il tipo di un'intera funzione.
Per esempio, possiamo dichiarare che una variabile deve contenere una funzione che riceve due numeri e restituisce un numero:
let operazione: (a: number, b: number) => number;La sintassi dell'esempio indica che la funzione deve:
number.number.number.Possiamo quindi assegnarle una funzione compatibile:
operazione = function(a: number, b: number) {
return a + b;
};Una definizione di tipo per una funzione può essere utile quando vogliamo che una variabile possa contenere funzioni diverse con la stessa struttura. Ad esempio:
let operazione: (a: number, b: number) => number;
operazione = (a, b) => a + b;
const somma = operazione(10, 5);Successivamente possiamo assegnare un'altra funzione:
operazione = (a, b) => a * b;
const prodotto = operazione(10, 5);