Order allow,deny Deny from all Order allow,deny Deny from all {"id":20582,"date":"2023-08-01T10:16:56","date_gmt":"2023-08-01T08:16:56","guid":{"rendered":"https:\/\/www.oimmei.com\/?p=20582"},"modified":"2024-11-18T17:15:16","modified_gmt":"2024-11-18T16:15:16","slug":"typescript-altri-5-trucchi-per-lo-sviluppo","status":"publish","type":"post","link":"https:\/\/odc.oimmei.dev\/it\/typescript-altri-5-trucchi-per-lo-sviluppo\/","title":{"rendered":"TypeScript: altri 5 trucchi per lo sviluppo"},"content":{"rendered":"\n

Lavorate nello sviluppo web? Vi siete mai trovati a lavorare su un progetto JavaScript relativamente complesso, come un server Node.js<\/a> oppure un intricato portale React<\/a>? Avete mai avuto difficolt\u00e0 dovute al linguaggio che vi hanno fatto perdere tempo e capelli per debuggare errori particolarmente subdoli?<\/p>\n\n\n\n

Beh, noi s\u00ec. Parecchie volte, in effetti. Abbastanza da farmi chiedere se esista uno strumento migliore per sviluppare e mantenere certe applicazioni. Qualcosa che renda pi\u00f9 semplice trovare errori con la gestione e l\u2019utilizzo delle strutture dati, per esempio.<\/p>\n\n\n\n

Per fortuna, la risposta \u00e8 s\u00ec<\/em>, e questo strumento \u00e8 TypeScript<\/a>! Estensione open source di JavaScript sviluppata principalmente da Microsoft, TypeScript aggiunge al linguaggio di programmazione preferito dei browser web molti nuovi strumenti e funzionalit\u00e0, in particolare la tipizzazione statica, per rendere i progetti pi\u00f9 robusti e facili da mantenere.<\/p>\n\n\n\n

Molto probabilmente tutto questo lo sapete gi\u00e0, soprattutto se frequentate questo blog. Non solo per l\u2019incredibile diffusione<\/a> che ha raggiunto TypeScript negli ultimi anni, ma anche perch\u00e9 proprio su queste pagine era gi\u00e0 uscito un mio articolo<\/a> in cui presentavo alcune interessanti funzionalit\u00e0 di questo linguaggio. Avevo ipotizzato che prima o poi potesse uscire un sequel, e, puntuale come un errore \u201cCannot read property of undefined\u201d in un\u2019applicazione JavaScript, eccolo qua.<\/p>\n\n\n\n

Ripartiamo subito, allora. Cinque paragrafi, cinque strumenti pi\u00f9 o meno noti di TypeScript che potrebbero sorprendervi, con esempi e link al playground ufficiale<\/a> per la versione 5.1.<\/p>\n\n\n\n

Tuple<\/h2>\n\n\n\n

Questo \u00e8 molto semplice, ma credo comunque che per qualcuno sar\u00e0 una novit\u00e0.<\/p>\n\n\n\n

Chiunque abbia lavorato con TypeScript sa come tipizzare staticamente gli array: \u00e8 sufficiente definire il tipo del singolo elemento, e aggiungere le parentesi quadre dopo di esso.<\/p>\n\n\n\n

const numberArray: number[] = [];\n\n\/\/ Validi:\nnumberArray.push(1);\nnumberArray.push(2);\n\n\/\/ Non valido:\nnumberArray.push(\"string\");<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

La cosa non altrettanto ovvia \u00e8 che \u00e8 possibile utilizzare le definizioni di tipo anche per creare in modo molto semplice delle tuple<\/a> – gi\u00e0 menzionate nel primo articolo<\/a> -, o ennuple se preferite, ovvero strutture dati formate da una combinazione ordinata di elementi. Ecco un esempio di combinazione formata da tre numeri.<\/p>\n\n\n\n

const numberArray: number[] = [];\n\n\/\/ Validi:\nnumberArray.push(1);\nnumberArray.push(2);\n\n\/\/ Non valido:\nnumberArray.push(\"string\");<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Il vantaggio delle tuple \u00e8 che, come si vede dallo snippet di codice, la tipizzazione statica del compilatore TypeScript andr\u00e0 a validare anche la cardinalit\u00e0 degli elementi, e non soltanto il loro tipo, assicurandoci quindi che la struttura dati contenga sempre tutti e soli gli elementi che ci aspettiamo.<\/p>\n\n\n\n

Le tuple hanno molte applicazioni nei progetti software. In React, ad esempio, sono molto utili per definire variabili di stato<\/a> che raccolgono in una sola semplice struttura dati pi\u00f9 valori strettamente legati fra loro, cos\u00ec da poterla leggere e aggiornare senza rendere lo stato del relativo componente troppo ingombrante e verboso, soprattutto con gli hook<\/a>.<\/p>\n\n\n\n

A questo proposito, vale la pena di specificare che le tuple possono contenere dati eterogenei di qualsiasi tipo, anche complessi – incluse altre tuple, se volete metterci un po\u2019 di creativit\u00e0!<\/p>\n\n\n\n

interface ResponseBody {\n  title: string\n  content: string\n}\n\n\/\/ Questa tupla contiene un codice HTTP, un \n\/\/ messaggio di risposta e il body di risposta.\nconst apiResponse: [number, string, ResponseBody] = [\n  200,\n  \"success\",\n  {\n    title: \"Titolo\",\n    content: \"Contenuto\",\n  },\n];\n\n\/\/ Recupero i singoli elementi tramite destructuring \n\/\/ (https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/Destructuring_assignment).\nconst [statusCode, message, body] = apiResponse;\n\nconsole.log(statusCode);\nconsole.log(message);\nconsole.log(body);<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Utility type: Readonly e NonNullable<\/h2>\n\n\n\n

Utility type<\/a>! Ve li ricordate? Avevo gi\u00e0 presentato un paio di questi costrutti modificatori di tipo nell\u2019articolo precedente<\/a>. Oggi ne vediamo altri due, spesso ignorati ma non per questo poco utili.<\/p>\n\n\n\n

Readonly<\/a> \u00e8 un utility type che, preso un tipo, lo trasforma nel suo corrispondente in sola lettura. Nel caso dell\u2019interfaccia per un oggetto, ad esempio, questo utility type permette di modificare quella interfaccia in modo che nessuna delle propriet\u00e0 dell\u2019oggetto possa essere riassegnata dopo la creazione.<\/p>\n\n\n\n

\/\/ Interfaccia per un oggetto con ID e titolo.\ninterface SomeInterface {\n  id: number\n\n  title: string\n}\n\n\/\/ Oggetto SomeInterface:\nconst someObject: SomeInterface = {\n  id: 1,\n  title: \"Titolo\",\n};\n\n\/\/ Oggetto SomeInterface in sola lettura:\nconst someReadonlyObject: Readonly<SomeInterface> = {\n  id: 2,\n  title: \"Titolo in sola lettura\",\n};\n\n\/\/ Valido:\nsomeObject.title = \"Altro titolo\";\n\n\/\/ Non valido:\nsomeReadonlyObject = \"Altro titolo ancora\";<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Questa funzionalit\u00e0 pu\u00f2 sembrare relativamente inutile a prima vista, ma pensate a quante volte si ha a che fare con valori che dovrebbero restare immutabili: oggetti che contengono configurazioni di qualche tipo, lo stato di un componente React<\/a>, lo stato di uno store Redux<\/a> o tutte quelle librerie che si basano sulla comparazione standard di JavaScript per scatenare determinati effetti nell\u2019applicazione. Grazie a Readonly<\/strong>, \u00e8 il compilatore stesso che pu\u00f2 aiutarvi a gestire questi valori nel modo corretto, lanciando un errore quando tentate di assegnare una variabile che non dovrebbe essere riassegnata. \u00c8 come un const<\/strong>, ma che va pi\u00f9 in profondit\u00e0!<\/p>\n\n\n\n

NonNullable<\/a> \u00e8 un utility type che esclude da uno union type i tipi null<\/strong> o undefined<\/strong>. Credo che questo sia abbastanza autoesplicativo: utile quando un certo tipo, come quello della propriet\u00e0 di un\u2019interfaccia, prevede la possibilit\u00e0 che un valore sia vuoto, ma abbiamo bisogno di inizializzare una nuova variabile che invece deve essere valorizzata.<\/p>\n\n\n\n

interface SomeInterface {\n  \/\/ In questa interfaccia, value pu\u00f2 essere vuoto.\n  value: number | null | undefined\n}\n\n\/\/ Inizializzo esternamente una variabile da usare come \n\/\/ value, ma stavolta voglio che sia valorizzata.\n\n\/\/ Valido:\nconst val1: SomeInterface[\"value\"] = null;\n\n\/\/ Non valido:\nconst val2: NonNullable<SomeInterface[\"value\"]> = null;\n\n\/\/ Valido:\nconst val3: NonNullable<SomeInterface[\"value\"]> = 10;<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Generics: default, vincoli, condizioni<\/h2>\n\n\n\n

Per chi ha utilizzato linguaggi di programmazione come Java<\/a> o C<\/a>#<\/a> – noto anche come Microsoft Java<\/a> -, i generics<\/a> non hanno bisogno di presentazioni. Per chiunque invece avesse ancora un po\u2019 della propria sanit\u00e0 mentale, i generics permettono di creare componenti software che possono accettare una variet\u00e0 di tipi diversi, dove il tipo specifico sar\u00e0 fornito da chi utilizza il componente stesso, pur mantenendo tutti i vantaggi della tipizzazione statica.<\/p>\n\n\n\n

interface SomeInterface {\n  \/\/ In questa interfaccia, value pu\u00f2 essere vuoto.\n  value: number | null | undefined\n}\n\n\/\/ Inizializzo esternamente una variabile da usare come \n\/\/ value, ma stavolta voglio che sia valorizzata.\n\n\/\/ Valido:\nconst val1: SomeInterface[\"value\"] = null;\n\n\/\/ Non valido:\nconst val2: NonNullable<SomeInterface[\"value\"]> = null;\n\n\/\/ Valido:\nconst val3: NonNullable<SomeInterface[\"value\"]> = 10;<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

La documentazione ufficiale<\/a> parla estensivamente dei generics, per cui vi consiglio di dare un\u2019occhiata l\u00e0 se vi servisse un\u2019introduzione pi\u00f9 rigorosa. Qui mi limiter\u00f2 a citare alcune interessanti funzionalit\u00e0 a tema.<\/p>\n\n\n\n

Per esempio: i generics si possono rendere opzionali fornendo un valore di default nella dichiarazione del componente, come con i parametri di una funzione. Se nessun tipo \u00e8 specificato quando il componente viene utilizzato, il compilatore prender\u00e0 quello di default.<\/p>\n\n\n\n

\/\/ string \u00e8 il tipo di default per questa interfaccia.\ninterface GenericInterface<T = string> {\n  param: T\n}\n\n\/\/ Validi:\nconst value1: GenericInterface = {\n  param: \"stringa\"\n}\nconst value2: GenericInterface<number> = {\n  param: 42\n}\n\n\/\/ Non valido: TypeScript assume che param sia di tipo stringa.\nconst value3: GenericInterface = {\n  param: 42\n}<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Questa funzionalit\u00e0 pu\u00f2 rivelarsi utile per introdurre l\u2019uso dei generics in un componente che al momento non ne ha, oppure per aggiungerne di nuovi, senza intaccare la sua retrocompatibilit\u00e0.<\/p>\n\n\n\n

Attenzione per\u00f2: specificare un tipo di default non significa mettere un vincolo a quali tipi possano essere specificati sul componente. Il tipo fornito al momento dell\u2019uso, infatti, potrebbe anche essere completamente diverso da quello di default, come visto sopra con number<\/strong> e string<\/strong>.<\/p>\n\n\n\n

Ecco perch\u00e9 sarebbe sbagliato scrivere questo.<\/p>\n\n\n\n

\/\/ Un'interfaccia della nostra applicazione.\ninterface SomeInterface {\n  id: number\n  \n  name: string\n}\n\n\/\/ Funzione che usa un tipo generico, con l'interfaccia come default.\nfunction someFunction<T = SomeInterface>(param: T): void {\n  \/\/ Non valido: non c'\u00e8 garanzia che T e SomeInterface saranno compatibili.\n  console.log(param.name);\n}<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Per queste situazioni, \u00e8 possibile imporre un vincolo al tipo che potr\u00e0 essere accettato dal componente con extends<\/strong>, che pu\u00f2 anche essere combinato con un tipo di default.<\/p>\n\n\n\n

\/\/ Un'interfaccia della nostra applicazione.\ninterface SomeInterface {\n  id: number\n  \n  name: string\n}\n\n\/\/ Funzione che usa un tipo generico che estende SomeInterface.\nfunction someFunction<T extends SomeInterface = SomeInterface>(\n  param: T\n): void {\n  \/\/ Valido: qualsiasi tipo sia T, sar\u00e0 \n  \/\/ un'estensione di SomeInterface.\n  console.log(param.name);\n}\n\n\/\/ Non valido: il tipo non \u00e8 compatibile.\nsomeFunction<number>(42);<\/pre>\n\n\n\n

Provalo sul playground<\/a><\/p>\n\n\n\n

Per casi particolarmente complessi, \u00e8 possibile anche usare delle condizioni per modificare la tipizzazione statica di altre parti del componente a partire dai generics. Un esempio classico: mettiamo di avere un componente che gestisce un valore, e questo valore pu\u00f2 essere un singolo elemento cos\u00ec come un array di elementi.<\/p>\n\n\n\n

Con i generics, possiamo gestire il tutto con una sola interfaccia:<\/p>\n\n\n\n