back to top

Le funzioni in TypeScript

Le funzioni svolgono un ruolo fondamentale in qualsiasi linguaggio di programmazione. Rispetto a Javascript, in TypeScript vengono aggiunte alcune funzionalità che rendono il codice più chiaro e leggibile e ciò e particolarmente evidente nella definizione di funzioni.

Annotazione dei tipi per le funzioni

Una prima novità è rappresentata dal sistema opzionale delle annotazioni di tipo di cui abbiamo già discusso nelle precedenti lezioni. In questo modo possiamo specificare in maniera esplicita il tipo dei parametri di una funzione e del valore di ritorno di quest’ultima. Vediamo un semplice esempio in cui illustriamo quanto abbiamo appena detto.

function lunghezzaStringa(stringa: string): number {
  return stringa.length;
}

Si tratta di una funzione che riceve in ingresso un argomento di tipo string e restituisce un valore numerico. Il valore restituito dalla funzione deve essere infatti di tipo number.

In alternativa possiamo sempre usare le espressioni di funzioni come in Javacript e aggiungere poi le annotazioni.

let lunghezzaStringa = function (stringa: string): number {
  return stringa.length;
};

In quest’ultimo caso è anche possibile specificare in maniera esplicita il tipo dell’intera funzione che è composto da due parti separate dalla coppia di caratteri ‘=>’. La prima parte contiene la definizione del tipo dei parametri, la seconda parte descrive il tipo del valore di ritorno della funzione.

let somma: (addendo1: number, addendo2: number) => number =
  function (a: number, b: number): number { return a + b; };

Abbiamo definito il tipo della funzione somma(). I nomi usati nel descrivere il tipo della funzione non devono coincidere con i nomi dei parametri della funzione stessa. Quello appena visto è un buon modo per documentare una funzione descrivendo esplicitamente quali argomenti ci si aspetta vengano passati e che tipo di valore viene restituito. Bisogna comunque evidenziare che il compilatore è in grado di inferire il tipo della funzione anche se si indicano esplicitamente soltanto i tipi dei parametri e del valore di ritorno. Allo stesso modo è comunque possibile scrivere il tipo della funzione e tralasciare i tipi dei i singoli parametri.

// Formato valido specificando il tipo dei parametri e del valore di ritorno
let somma = function (a: number, b: number): number { return a + b; };

// Formato valido specificando il tipo della funzione 
let somma: (addendo1: number, addendo2: number) => number =
  function (a , b) { return a + b; };

Arrow function in TypeScript

Come per ES2015, TypeScript supporta le cosiddette Arrow Function che possono essere usate in vari contesti come nel caso in cui si voglia passare una funzione come argomento di un’altra funzione. Al di là della sintassi, sono diverse le novità introdotte dalle Arrow Function. Una di queste è rappresentata dal valore che assume la variabile this. In questa lezione non tratteremo questo argomento, ma potete trovare maggiori informazioni sul sito di Mozilla.

Parametri opzionali e di default

Al contrario di Javascript, ogni parametro di una funzione è obbligatorio se non diversamente specificato. Ciò vuol dire che il numero di argomenti che vengono passati a una funzione deve coincidere col numero di parametri stabiliti in fase di definizione della funzione stessa. Se un argomento è null o undefined bisogna indicarlo esplicitamente.

function log(a: string, b: string, c: string) {
  console.log(`a: ${a}, b: ${b}, c: ${c}`);
}

//  Expected 3 arguments, but got 2
log('a', 'b'); // ERROR

log('a', 'b', undefined); // OK

log('a', 'b', 'c'); // OK

È comunque possibile indicare che uno o più parametri sono opzionali attraverso l’uso del carattere ‘?’ affiancato al nome del parametro che si vuole segnalare come non obbligatorio. In questo caso, se a una funzione non passiamo gli argomenti opzionali, questi ultimi assumeranno il valore undefined.

function log(a: string, b: string, c?: string) {
  console.log(`a: ${a}, b: ${b}, c: ${c}`);
}

log('a', 'b'); // OK - c è undefined

log('a', 'b', undefined); // OK - c è undefined

log('a', 'b', 'c'); // OK

Nell’usare i parametri opzionali, bisogna fare attenzione ad alcuni accorgimenti. Per esempio, nella dichiarazione di una funzione, un parametro opzionale non può precedere uno obbligatorio. Il compilatore provvederà comunque a segnalare eventuali errori.

// A required parameter cannot follow an optional parameter
// ERRORE: a è opzionale, ma b è obbligatorio
function log(a?: string, b: string, c?: string) { 
  console.log(`a: ${a}, b: ${b}, c: ${c}`);
}

Abbiamo detto che, non passando a una funzione un argomento opzionale, quest’ultimo assumerà il valore undefined. È possibile correggere tale comportamento usando i valori di default. Il valore di default, specificato per un parametro, sarà assegnato a quest’ultimo se non viene passato un valore o se il suo valore non è definito.

function log(a: string, b: string, c = '-') {
  console.log(`a: ${a}, b: ${b}, c: ${c}`);
}

log('a', 'b'); // a: a, b: b, c: -

log('a', 'b', undefined); // a: a, b: b, c: -

log('a', 'b', 'c'); // a: a, b: b, c: c

L’operatore Rest

Come in ES2015, TypeScript supporta l’operatore Rest.

L’operatore Rest viene espresso attraverso l’uso di tre punti seguiti dal nome di un parametro (…nome_parametro). Gli argomenti passati a una funzione in corrispondenza dell’operatore Rest vengono accumulati all’interno di un array. In questo modo la funzione può ricevere un numero indefinito di argomenti. Per esempio, nel frammento di codice riportato sotto, i primi due argomenti vengono sempre assegnati rispettivamente ad ‘a’ e ‘b’. Altri eventuali argomenti sono inseriti all’interno di un array (args). L’operatore Rest viene quindi usato in fase di definizione di una funzione per consentire di passare alla funzione stessa un numero indefinito di argomenti.

function log(a: string, b: string, ...args: string[]) {
  console.log(`a: ${a}, b: ${b}, args: ${args}`);
}

log('a', 'b'); // a: a, b: b, args:

log('a', 'b', undefined, 'd'); // a: a, b: b, args: ,d

log('a', 'b', 'c', 'd', 'e'); // a: a, b: b, args: c,d,e

Overloading delle funzioni in TypeScript

Javascript è un linguaggio flessibile a tipizzazione dinamica che consente di passare a una funzione argomenti di tipo diverso. È inoltre possibile omettere un certo argomento che assumerà quindi il valore undefined. In questo modo si può invocare una funzione con argomenti che possono variare in numero e tipo. La funzione dovrà quindi essere implementata in maniera opportuna per definire un diverso comportamento a seconda degli argomenti ricevuti.

In TypeScript abbiamo visto che, una volta definita, una funzione aspetta un numero determinato di argomenti che devono essere del tipo stabilito in fase di definizione. È possibile rendere una funzione un po’ più flessibile usando i parametri opzionali (o quelli di default) e il sistema, visto nelle precedenti lezioni, dell’unione dei tipi. In ogni caso TypeScript permette di dichiarare tipi diversi per ogni funzione in quella che prende il nome di overload list. Il compilatore analizza la lista cercando di trovare la versione corretta della funzione da invocare in base agli argomenti passati. Possiamo perciò definire più funzioni con lo stesso nome, ma aventi parametri diversi per numero e tipo. Anche il valore di ritorno può essere differente per le varie funzioni della lista. Dovremo quindi seguire due semplici passi:

  1. Elencare le varie versioni di una funzione senza il corpo della funzione, ma esplicitando il tipo dei parametri e dei valori di ritorno.
  2. Definire immediatamente dopo una funzione che abbia lo stesso nome delle precedenti e che presenti dei parametri e un valore di ritorno di un tipo compatibile con quello delle funzioni elencate. Nel corpo di quest’ultima funzione definiamo un diverso comportamento a seconda degli argomenti ricevuti.
function somma(addendo1: number, addendo2: number): number;
function somma(addendi: number[]): number;
function somma(a: number | number[], b?:number): number {
  if (typeof a === "number") {
    return a + b;
  }
  return a.reduce(
    (
      parziale: number, 
      valoreCorrente: number, 
      indiceCorrente: number, 
      a: number[]) => {
        return parziale + valoreCorrente;
  });
}

console.log(somma(11, 22)); // 33
console.log(somma([6, 12, 18, 36])); // 72

Conclusioni

In questa lezione abbiamo illustrato brevemente come usare le funzioni in TypeScript. Abbiamo quindi mostrato alcune funzionalità non presenti in ES5. Nella prossima lezione parleremo di un altro argomento fondamentale, ovvero delle classi.

Pubblicità
Articolo precedente
Articolo successivo