Come risparmiare tempo in React con TypeScript

Come utilizzare TypeScript in React per risparmiare sul tempo di sviluppo

1/10/2020

Se avete mai programmato in JavaScript, saprete perfettamente che un programmatore ha un rapporto di affetto molto profondo nei confronti dei Type Error: dimentica il nome di una variabile, di che tipo sia, o non ricorda cosa va passato ad un metodo. Questo potrebbe non essere un grosso problema se parliamo di piccole applicazioni e piccoli team. Ma se il volume di codice comincia a crescere e, come spesso accade in React, i componenti iniziano a moltiplicarsi, allora questo rapporto d’amore comincia a barcollare e queste piccole sviste e dimenticanze iniziano a consumare un bel po’ del nostro tempo.

Vediamo un esempio in React (versione > 16.8), creato con Create React App. Nella nostra app avremo 4 funzionalità: visualizzare la lista, aggiungere un nuovo elemento, eliminare un elemento dalla lista e visualizzare il paese di riferimento di quella valuta cliccando su un elemento.

alt text

Ogni valuta ha la seguente struttura:

{
  id: 3,
  name: 'RUB',
  value: 81.18,
  at: new Date()
}

Nel dettaglio: name è il nome della valuta, value è il valore corrispondente ad 1 euro, at è la data in cui è stato registrato quel valore.

Lo stato dell’applicazione è gestito, tramite useState, in questo modo:

const [currencies, setCurrencies] = useState(data);
const [newCurrency, setNewCurrency] = useState({
  id: data.length,
  name: "",
  value: 1,
  at: null,
});
const [info, setInfo] = useState("");

Immaginate di essere una nuova leva del team e di leggere questo snippet di codice per la prima volta. Indipendentemente dal vostro livello di scrupoli, vi dovrebbero sorgere almeno due domande:

  • at che cosa rappresenta?
  • com’è fatto l’array di currencies?

Perché in fondo è questo il problema: quando abbiamo a che fare con stringhe, numeri e booleani, è tutto bellissimo, riusciamo ad applicare mentalmente una type inference e a dedurre il tipo di queste variabili. Ma per null cosa possiamo fare?

Vediamo un altro esempio con il form per l’aggiunta di una nuova valuta:

function AddItem(props) {
  return (
    <form onSubmit={props.handleSubmit}>
      <h1>Aggiungi nuova valuta</h1>
      <div>
        <label>Nome</label>
        <div>
          <input
            name="name"
            value={props.item.name}
            onChange={props.handleChange}
            type="text"
          />
        </div>
      </div>
      <div>
        <label>Valore corrispondente ad 1 EURO</label>
        <div>
          <input
            name="value"
            value={props.item.value}
            onChange={props.handleChange}
            type="number"
            min={1}
            step={0.01}
          />
        </div>
      </div>
      <button type="submit">Aggiungi</button>
    </form>
  );
}

Anche per i non appassionati di mistero, quel props lì in cima dovrebbe incuriosire un bel po’.

Questi problemi non sono nuovi, naturalmente, e si possono limitare in diversi modi: in minima parte già con un buon IDE, oppure utilizzando un linter o con la libreria prop-types, tanto per citarne alcuni.

Per fortuna esiste anche TypeScript. Indipendentemente da tutte le funzionalità che offre allo sviluppatore, ai suoi vantaggi e anche ai suoi svantaggi - che non tratteremo in questo breve articolo e che andrebbero considerati in base al progetto e al team di cui si dispone - concentriamoci solo su una delle tagline che questo superset di JavaScript offrirebbe:

TypeScript makes code more readable and debuggable

Ovvero: codice più leggibile e più facile da debuggare. Proviamo a riscrivere la nostra applicazione, stavolta utilizzando TypeScript. Lanciamo il comando:

npx create-react-app example2 --template typescript

che farà già tutta la configurazione al posto nostro. Ora creiamo un file types.d.ts in /src, e definiamo un tipo Currency per renderlo disponibile in tutta l’applicazione.:

type Currency = {
  id: number;
  name: string;
  value: number;
  at: Date | null;
};

Nel nostro componente App lo stato sarà il seguente:

const [currencies, setCurrencies] = useState<Currency[]>(data);
const [newCurrency, setNewCurrency] = useState<Currency>({
id: data.length,
name: '',
value: 1,
at: null
});
const [info, setInfo] = useState('');

Se ora leggessimo il codice per la prima volta sapremmo perfettamente ogni variabile che cosa contiene e che cosa dovrà gestire. Per il nostro form potremmo dichiarare un tipo per le props del componente, il quale deve ricevere:

  • un item (di tipo Currency)
  • una funzione handleChange
  • una funzione handleSubmit

Entrambe le funzioni ricevono, in ingresso, un evento del form e non restituiscono nulla. Una bozza della definizione di AddItemProps potrebbe essere:

type AddItemProps = {
  item: Currency,
  handleChange: (event) => void
  handleSubmit: (event) => void
}

Ma event lasciato così, senza un tipo, non va bene, né per noi né per TypeScript, che ci obbliga ad assegnare un tipo ad ogni input. Ragioniamoci un secondo:

  • handleChange cattura un evento change dagli input del form
  • handleSubmit cattura un evento submit dal form

A questo punto, riscriviamo le nostre props in questo modo:

type AddItemProps = {
  item: Currency,
  handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void
  handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}

Non c’è bisogno di continuare: risulta chiaro che ora possiamo definire i tipi degli ingressi e delle uscite dei nostri componenti, dei metodi e delle variabili in generale. Inoltre, TypeScript ci segnalerà eventuali sviste o ci aiuterà a ricordare nomi e proprietà.

Qui sotto, ad esempio, dopo il . ci segnala l’elenco delle proprietà dell’item che abbiamo definito una riga sopra con il tipo Currency.

alt text

In quest’altra immagine, invece, ci segnala che value non è una stringa ma un numero e che stiamo sbagliando ad inizializzarla.

alt text

Tutto fantastico, certo, ma potreste pensare: “Non è quello che faccio già con la libreria prop-types?”. Beh, no, c’è una differenza sostanziale. La libreria prop-types effettua un controllo runtime; TypeScript, invece, agisce in compile time. Insomma: ci aiuta mentre scriviamo il codice!

Articoli correlati

Vedi tutti