Order allow,deny Deny from all Order allow,deny Deny from all {"id":18507,"date":"2022-12-21T10:26:26","date_gmt":"2022-12-21T09:26:26","guid":{"rendered":"https:\/\/www.oimmei.com\/?p=18507"},"modified":"2024-11-18T17:04:02","modified_gmt":"2024-11-18T16:04:02","slug":"typescript-5-trucchi-sviluppo","status":"publish","type":"post","link":"https:\/\/odc.oimmei.dev\/it\/typescript-5-trucchi-sviluppo\/","title":{"rendered":"TypeScript: 5 trucchi per lo sviluppo"},"content":{"rendered":"\n

Questo articolo su TypeScript sarebbe dovuto uscire molto tempo fa, ma abbiamo avuto qualche difficolt\u00e0 a farlo compilare. Abbiamo finalmente risolto tutti gli errori di tipo, quindi ecco a voi.<\/p>\n\n\n\n

TypeScript<\/h2>\n\n\n\n

Ah, TypeScript. Il linguaggio di programmazione open source sviluppato da Microsoft come estensione di JavaScript ormai non ha pi\u00f9 bisogno di presentazioni. Con la crescente popolarit\u00e0 di tecnologie come React<\/a>, Angular<\/a> e Node.js<\/a>, la necessit\u00e0 di sviluppare applicativi JavaScript robusti e facili da mantenere si \u00e8 fatta sempre pi\u00f9 difficile da ignorare, e la tipizzazione statica fornita da TypeScript d\u00e0 sicuramente una grossa mano per questo.<\/p>\n\n\n\n

Se fate parte del sempre pi\u00f9 ristretto<\/a> gruppo di sviluppatori e sviluppatrici web che non hanno ancora adottato TypeScript, non preoccupatevi: per chi conosce JavaScript, il suo cugino staticamente tipato non \u00e8 difficile da approcciare. Essendo un\u2019estensione, TypeScript pu\u00f2 nativamente compilare qualunque applicazione scritta in JavaScript – con l\u2019eccezione di alcuni errori di tipo -, permettendo anche di passare progressivamente alle sue nuove funzionalit\u00e0.<\/p>\n\n\n\n

Lo scopo di questo articolo, per\u00f2, non \u00e8 introdurre TypeScript, anche perch\u00e9 ci\u00f2 sarebbe ridondante per una buona parte di voi, bens\u00ec mostrare alcune interessanti funzionalit\u00e0 che ho pian piano scoperto nel corso del mio lavoro con il linguaggio diretto da Anders Hejlsberg<\/a>. Al di l\u00e0 delle meccaniche pi\u00f9 palesi, infatti, TypeScript ha molte possibilit\u00e0 non ovvie anche per chi ha una discreta esperienza con il suo utilizzo, e che possono risultare veramente molto utili per rendere le nostre applicazioni ancora pi\u00f9 robuste e leggibili oppure per risparmiare codice non necessario.<\/p>\n\n\n\n

Ecco quindi cinque trucchi da usare nei vostri progetti TypeScript, a partire dai pi\u00f9 noti e diffusi fino ad arrivare a quelli un po\u2019 pi\u00f9 oscuri e situazionali, completi di esempi sul playground ufficiale<\/a>, tutti garantiti sull\u2019attuale versione del linguaggio (4.8).<\/p>\n\n\n\n

Utility type: Omit e Pick<\/h3>\n\n\n\n

Gli utility type sono tipi speciali disponibili globalmente in qualsiasi applicativo TypeScript. Si tratta di costrutti che, partendo da un tipo, ad esempio un’interfaccia, possono produrne un secondo modificando in qualche maniera il primo.<\/p>\n\n\n\n

Esistono molti utility type e sono ben documentati nella guida ufficiale<\/a>, quindi in questo articolo mi limiter\u00f2 a illustrare due fra quelli che ho trovato pi\u00f9 utili nei nostri progetti.<\/p>\n\n\n\n

Omit<\/a> \u00e8 un utility type che, preso un tipo A con una serie di propriet\u00e0, lo trasforma in un tipo B rimuovendo tutte le propriet\u00e0 specificate nella definizione.<\/p>\n\n\n\n

\/\/ Tipo originale:\ninterface TypeA {\n  id: string\n\n  name: string\n\n  description: string\n\n  price: number\n}\n\n\/\/ Creo un nuovo tipo dall'originale, escludendo le propriet\u00e0 description e price:\ntype TypeB = Omit<TypeA, \"description\" | \"price\">;\n\n\/\/ Creo un oggetto del nuovo tipo:\nconst objB: TypeB = {\n  id: \"1001\", \/\/ Valido.\n\n  name: \"Nome\", \/\/ Valido.\n\n  description: \"Descrizione\", \/\/ Non valido: questa propriet\u00e0 non esiste in TypeB.\n}<\/pre>\n\n\n\n

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

Omit \u00e8 molto utile in situazioni in cui si vogliono costruire oggetti parziali di un certo tipo, ma mantenendo i vincoli imposti dal tipo stesso, come le propriet\u00e0 obbligatorie. Per questo, pu\u00f2 rivelarsi pi\u00f9 robusto e preciso di Partial<\/a>, che invece si limita a rendere opzionali tutte le propriet\u00e0, anche se meno immediato.<\/p>\n\n\n\n

Altro campo in cui Omit aiuta molto \u00e8 il riutilizzo del codice. Immaginate questo scenario: nella vostra applicazione, magari in una libreria di terze parti, esiste gi\u00e0 un tipo TextInputProps<\/strong>, che \u00e8 un\u2019interfaccia per le propriet\u00e0 di un input testuale. Volete creare un nuovo input, identico al primo, ma che gestisce solo numeri e non stringhe. Si potrebbe pensare di estendere TextInputProps<\/strong> per cambiare il tipo di value<\/strong>, ma\u2026<\/p>\n\n\n\n

interface TextInputProps {\n  label: string\n\n  name: string\n\n  value: string\n}\n\ninterface NumberInputProps extends TextInputProps {\n  value: number\n}\n<\/pre>\n\n\n\n

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

Oh, no! Non possiamo estendere l\u2019interfaccia perch\u00e9 i tipi di value<\/strong> non sono compatibili! E adesso? Dobbiamo per forza replicare l\u2019interfaccia, oppure dividere la prima per separare i campi in comune?<\/p>\n\n\n\n

No: Omit viene in nostro soccorso. Invece di estendere TextInputProps<\/strong>, estenderemo una versione trasformata, che ha tutti i campi tranne value<\/strong>. Questo ci permette di ridefinire senza problemi la propriet\u00e0, anche senza rispettare i vincoli dell\u2019interfaccia madre. La nuova interfaccia non sar\u00e0 pi\u00f9 un\u2019estensione della prima in termini di programmazione orientata agli oggetti – in altre parole: oggetti di tipo NumberInputProps<\/strong> non saranno anche di tipo TextInputProps <\/strong>-, ma in questo caso la cosa non ci interessa.<\/p>\n\n\n\n

interface TextInputProps {\n  label: string\n\n  name: string\n\n  value: string\n}\n\ninterface NumberInputProps extends Omit<TextInputProps, \"value\"> {\n  value: number\n}\n<\/pre>\n\n\n\n

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

Una cosa in meno da fare prima di tornare a casa a giocare alla PlayStation! Woo-ooh!<\/p>\n\n\n\n

Come avrete intuito, Pick<\/a> ha il funzionamento diametralmente opposto, ovvero permette di costruire un secondo tipo prendendo soltanto un sottoinsieme delle propriet\u00e0 del primo, e si rivela utile allo stesso modo.<\/p>\n\n\n\n

\/\/ Tipo originale:\ninterface TypeA {\n  id: string\n\n  name: string\n\n  description: string\n\n  price: number\n}\n\n\/\/ Creo un nuovo tipo dall'originale, prendendo solo le propriet\u00e0 id e name:\ntype TypeB = Pick<TypeA, \"id\" | \"name\">;\n\n\/\/ Creo un oggetto del nuovo tipo:\nconst objB: TypeB = {\n  id: \"1001\", \/\/ Valido.\n\n  name: \"Nome\", \/\/ Valido.\n\n  description: \"Descrizione\", \/\/ Non valido: questa propriet\u00e0 non esiste in TypeB.\n}\n<\/pre>\n\n\n\n

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

(Nota per i so-tutto-io che si stavano gi\u00e0 scrocchiando le dita: s\u00ec, so che la prima situazione si risolverebbe molto meglio con i generics type<\/a>, era solo un esempio. Tenete gi\u00f9 le mani dalla tastiera e stasera non vi umilier\u00f2 in Elden Ring.)<\/p>\n\n\n\n

Accedere al tipo di una propriet\u00e0<\/h3>\n\n\n\n

Questa \u00e8 molto semplice, ma riesce comunque a evitare di replicare alcune informazioni nel codice.<\/p>\n\n\n\n

Ecco una situazione abbastanza comune in un\u2019applicazione TypeScript relativamente complessa: immaginate di avere un\u2019interfaccia, con una serie di propriet\u00e0 ciascuna con il proprio tipo. Dovete creare un oggetto che implementa questa interfaccia, ma uno dei valori \u00e8 particolarmente complicato da calcolare, e preferite farlo prima in una variabile per poi usare questa variabile come valore. La domanda \u00e8: come fare a rendere questa variabile staticamente tipata con il tipo giusto?<\/p>\n\n\n\n

La risposta pi\u00f9 semplice \u00e8 banalmente prendere il tipo della relativa propriet\u00e0 dell\u2019interfaccia e ripeterlo all\u2019inizializzazione della variabile.<\/p>\n\n\n\n

\/\/ Interfaccia:\ninterface SomeInterface {\n  id: number\n\n  code: string\n\n  name: string\n}\n\n\/\/ Dichiaro una variabile per calcolare il valore di code:\nlet code: string; \/\/ <- Tipo della propriet\u00e0 ripetuto\n\n\/\/ Logica di inizializzazione incredibilmente complessa:\ncode = \"CODICE\";\n\nconst someObject: SomeInterface = {\n  id: 1,\n  code: code,\n  name: \"Nome\",\n}<\/pre>\n\n\n\n

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

Questa replicazione, per\u00f2, non \u00e8 necessaria. Quando abbiamo un tipo che ha delle propriet\u00e0, come un\u2019interfaccia, TypeScript ci permette di fare riferimento direttamente al tipo di una specifica propriet\u00e0 usando le parentesi quadre, come se stessimo accedendo a un comune oggetto JavaScript.<\/p>\n\n\n\n

\/\/ Interfaccia:\ninterface SomeInterface {\n  id: number\n\n  code: string\n\n  name: string\n}\n\n\/\/ Dichiaro una variabile per calcolare il valore di code:\nlet code: SomeInterface[\"code\"]; \/\/ <- Nessuna replicazione!\n\n\/\/ Logica di inizializzazione incredibilmente complessa:\ncode = \"CODICE\";\n\nconst someObject: SomeInterface = {\n  id: 1,\n  code: code,\n  name: \"Nome\",\n}<\/pre>\n\n\n\n

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

Solo con le parentesi quadre, per\u00f2: non provateci con la notazione puntata.<\/p>\n\n\n\n

Cos\u00ec facendo, sar\u00e0 l\u2019interfaccia la sola ad avere l\u2019informazione sul tipo della propriet\u00e0, e se quest\u2019ultimo in futuro cambiasse avremmo una cosa in meno da modificare. Intendiamoci: se la propriet\u00e0 passa da un primo tipo a un secondo non compatibile dovremo in ogni caso correggere l\u2019inizializzazione del valore, ma non avremo da preoccuparci della dichiarazione della variabile.<\/p>\n\n\n\n

Allo stesso modo, \u00e8 possibile accedere al tipo di un elemento di un array usando il tipo del suo indice, ovvero number<\/em>.<\/p>\n\n\n\n

\/\/ Interfaccia:\ninterface SomeInterface {\n  id: number\n\n  code: string\n\n  name: string\n}\n\n\/\/ Dichiaro un array di oggetti di tipo SomeInterface:\ntype SomeInterfaceArray = SomeInterface[];\n\n\/\/ Inizializzo una variabile, usando l'array per prendere il tipo:\nconst someObject: SomeInterfaceArray[number] = {\n  id: 1,\n  code: \"CODICE\",\n  name: \"Nome\",\n}<\/pre>\n\n\n\n

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

Type narrowing: discriminated union<\/h3>\n\n\n\n

Una delle mie funzionalit\u00e0 preferite, e una davvero interessante per chi proviene dai classici linguaggi di programmazione orientati agli oggetti.<\/p>\n\n\n\n

Tanto per cominciare: non credo che gli union type<\/a> siano un mistero per nessuno di noi. Si tratta di quella funzionalit\u00e0 che permette di costruire un nuovo tipo combinando tipi esistenti con l\u2019operatore pipe (|).<\/p>\n\n\n\n

\/\/ Union type:\ntype SomeUnion = string | boolean;\n\n\/\/ Valido:\nconst someVariable1: SomeUnion = \"Stringa\";\n\n\/\/ Valido:\nconst someVariable2: SomeUnion = false;\n\n\/\/ Non valido:\nconst someVariable3: SomeUnion = 10;<\/pre>\n\n\n\n

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

Altrettanto noto \u00e8 il fatto che, dato che i valori scalari in TypeScript sono considerati tipi validi, \u00e8 possibile costruire degli union type che permettano di valorizzare una variabile o una propriet\u00e0 con uno di una serie di valori imposti dal tipo stesso, ad esempio una fra tre stringhe.<\/p>\n\n\n\n

\/\/ Union type:\ntype SomeUnion = \"stringa_1\" | \"stringa_2\" | \"stringa_3\";\n\n\/\/ Valido:\nconst someVariable1: SomeUnion = \"stringa_1\";\n\n\/\/ Non valido:\nconst someVariable2: SomeUnion = \"stringa_5\";<\/pre>\n\n\n\n

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

Tutto abbastanza noioso fin qui, ma datemi un attimo; ora arriva il bello. Sapevate che \u00e8 possibile usare gli union type per discriminare fra diversi tipi e fare s\u00ec che il compilatore riconosca la tipizzazione statica? Un po\u2019 contorto da spiegare a parole, lo so. Facciamo di nuovo un esempio.<\/p>\n\n\n\n

Mettiamo che il nostro progetto TypeScript chiami o esponga un\u2019API REST. La risposta di questa API contiene:<\/p>\n\n\n\n