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 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 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 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 Provalo sul playground<\/a><\/p>\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 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 Provalo sul playground<\/a><\/p>\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 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 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 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 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 Ecco il risultato.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n Dopo il primo<\/a> articol<\/a>o<\/a>, torniamo a parlare di type narrowing<\/a>, questa volta facendo un po\u2019 pi\u00f9 di giustizia a questa importantissima funzionalit\u00e0.<\/p>\n\n\n\n La scorsa volta, parlando delle discriminated u<\/a>nion<\/a>, avevo solo brevemente menzionato l\u2019argomento del type narrowing, dicendo in genere che si tratta del meccanismo con cui TypeScript riesce a dedurre, dalla tipizzazione che definiamo e dal flusso della nostra applicazione, quale tipo avr\u00e0 una specifica variabile a runtime, cos\u00ec da presentarci gli errori appropriati durante la compilazione. Quello che non avevo detto \u00e8 quanto dannatamente potente e onnipresente sia questo meccanismo nelle applicazioni TypeScript, e quanto spesso lo utilizziamo senza nemmeno rendercene conto.<\/p>\n\n\n\n Pensiamo a una funzione con un parametro che pu\u00f2 essere una stringa oppure un numero. Se riceviamo una stringa, vogliamo stampare la sua lunghezza; se invece riceviamo un numero, vogliamo stamparne il valore in notazione puntata<\/a>. La nostra conoscenza di JavaScript ci porta naturalmente a scrivere del codice come questo, usando l\u2019operatore typeof<\/a>.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n Molto semplice, vero? Eppure, diverse cose tutt\u2019altro che banali stanno succedendo sotto la superficie di questo snippet.<\/p>\n\n\n\n All\u2019inizio della funzione, abbiamo indicato che il parametro value<\/strong> pu\u00f2 essere una stringa oppure un numero, tramite uno dei nostri adorabili union type<\/a>. Ma, nei due rami dell\u2019if<\/em>, stiamo usando delle funzionalit\u00e0 non comuni a questi due tipi: nel ramo then<\/em> usiamo length<\/strong>, propriet\u00e0 che non esiste nel tipo number<\/strong>, mentre nel ramo else<\/em> usiamo toFixed<\/strong>, metodo che non esiste nel tipo string<\/strong>. <\/p>\n\n\n\n Se proviamo a eliminare l\u2019if<\/em>, vediamo che entrambi i suoi rami restituiscono giustamente un errore.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n Nella prima versione, per\u00f2, TypeScript non segnala nessuna anomalia. Cosa c\u2019\u00e8 sotto?<\/p>\n\n\n\n La risposta sta proprio nel type narrowing. TypeScript esamina il nostro codice e, dagli operatori che usiamo e dal flusso dell\u2019applicazione, si rende conto che in determinati punti dell\u2019esecuzione un certo valore avr\u00e0 un tipo pi\u00f9 specifico rispetto a quello che abbiamo dichiarato.<\/p>\n\n\n\n L\u2019operatore typeof<\/strong>, ad esempio, costituisce quello che si chiama una type guard<\/em>, ovvero un operatore di controllo speciale che ha effetto sul tipo che TypeScript deduce per un certo valore. Ecco perch\u00e9 la prima versione della funzione sopra non restituisce errori: partendo dallo union type string | number<\/strong>, TypeScript vede la condizione dell\u2019if<\/em> e capisce non soltanto che nel ramo then<\/em> value<\/strong> sar\u00e0 certamente di tipo string<\/strong>, ma anche che nel ramo else<\/em> value<\/strong> sar\u00e0 per esclusione di tipo number<\/strong>.<\/p>\n\n\n\n Il type narrowing \u00e8 cruciale per le applicazioni TypeScript, e si applica a un gran numero di costrutti diversi: i typeof<\/strong>, ma anche i confronti, gli assegnamenti, operatori come in<\/a> e instanceof<\/a>, negli if<\/em>, negli switch<\/em>\u2026<\/p>\n\n\n\n Ma voi non siete qui per lunghe spiegazioni sul funzionamento di TypeScript. Voi volete qualche dritta su quelle funzionalit\u00e0 che potete usare nel vostro codice e raccontare alle feste per ottenere credito, rispetto e ammirazione dai vostri amici, e io vi accontento subito.<\/p>\n\n\n\n (Nota: la conoscenza di quanto spiegato di seguito potrebbe non farvi effettivamente ottenere credito, rispetto e ammirazione dai vostri amici. L\u2019autore dell\u2019articolo declina ogni responsabilit\u00e0 riguardo la scarsa riuscita della vostra vita sociale.)<\/p>\n\n\n\n Ci sono molti casi in cui sfruttare il type narrowing non \u00e8 cos\u00ec banale. Certo, finch\u00e9 si parla di tipi primitivi<\/a> come stringhe e numeri, oppure di utility built-in come Date<\/strong>, allora \u00e8 tutto molto semplice, ma\u2026 se ci fosse bisogno di lavorare con interfacce definite da noi?<\/p>\n\n\n\n Tenete a mente che le interfacce TypeScript hanno un piccolo grande problema: non esistono a runtime. In effetti, non esistono e basta, dato che le interfacce al momento non esistono in JavaScript. Sono un aiuto per il compilatore per rilevare errori di tipo statici, ma vengono eliminate nel corso della compilazione. Quindi no, non possiamo semplicemente usare instanceof<\/strong> come faremmo con le interfacce di altri linguaggi orientati agli oggetti; dobbiamo metterci un po\u2019 di impegno in pi\u00f9.<\/p>\n\n\n\n Un caso tipico: abbiamo un\u2019interfaccia SomeInterface<\/strong>, una seconda interfaccia SomeExtension<\/strong> che estende la prima e una funzione che prende oggetti di tipo SomeInterface<\/strong>. Se l\u2019oggetto che riceviamo ha tipo SomeExtension<\/strong>, vogliamo fare delle operazioni supplementari all\u2019interno della funzione. Peccato che l\u2019operatore in<\/strong> non basti a convincere TypeScript delle nostre buone intenzioni.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n (Fino a TypeScript 4.8, avremmo avuto un errore anche per l\u2019accesso a description<\/strong>. Nella versione 4.9 c\u2019\u00e8 stata qualche modifica al type narrowing, che permette di accedere in sicurezza al campo specificamente testato con in<\/strong>, anche se il tipo dedotto sar\u00e0 unknown<\/strong>.)<\/p>\n\n\n\n Quello che dobbiamo fare qui \u00e8 far capire a TypeScript che il nostro accesso alle propriet\u00e0 dell\u2019estensione \u00e8 giustificato, o, se preferite, che stiamo usando queste propriet\u00e0 solo quando obj<\/strong> \u00e8 effettivamente di tipo SomeExtension<\/strong>. Per fortuna, questo \u00e8 possibile con i type predicate<\/a>.<\/p>\n\n\n\n In sostanza, i type predicate ci permettono di definire delle type guard personalizzate, con condizioni arbitrariamente complesse, che garantiscono a TypeScript che in un certo blocco della nostra applicazione una determinata variabile sia di un certo tipo – proprio come l\u2019operatore typeof<\/strong>, ma con una logica interamente definita da noi.<\/p>\n\n\n\n Creiamo una funzione isSomeExtension<\/strong>, che prende in ingresso un parametro di tipo SomeInterface<\/strong> e restituisce un type predicate che stabilisce che il parametro in ingresso \u00e8 di tipo SomeExtension<\/strong>. Nel body, la funzione deve esaminare il parametro<\/a> e restituire true<\/strong> se il predicato in uscita \u00e8 valido, false<\/strong> altrimenti.<\/p>\n\n\n\n Infine, usiamo quella funzione nella condizione di someFunction<\/strong>.<\/p>\n\n\n\n Provalo sul <\/a>playground<\/a><\/p>\n\n\n\n Tutto corretto!<\/p>\n\n\n\n Un appunto importante in chiusura di questo lungo paragrafo: tenete conto che con i type predicate stiamo praticamente \u201csaltando\u201d i controlli di tipo che TypeScript ci offre, e che il compilatore si fider\u00e0 completamente di noi per quanto riguarda il funzionamento della type guard. Ci\u00f2 significa che dobbiamo fare molta attenzione a scrivere la funzione che controlla il tipo: se sbagliamo qualche condizione nel body, ce ne renderemo conto solo dai bug a runtime!<\/p>\n\n\n\n Chi proviene da linguaggi come Java conoscer\u00e0 il method overloading<\/a>: si tratta di quella funzionalit\u00e0 che permette di specificare, all\u2019interno di una classe o di un\u2019interfaccia, pi\u00f9 metodi con lo stesso nome e diversi insiemi di parametri – per esempio un numero diverso di parametri, oppure parametri di tipo differente.<\/p>\n\n\n\n Forse non tutti sanno che<\/em>\u2122 una funzionalit\u00e0 molto simile esiste anche in TypeScript, e pu\u00f2 essere davvero utile in certe situazioni. Sto parlando del function overload<\/a>, utilizzabile sia sulle funzioni che sui metodi di una classe.<\/p>\n\n\n\n Il funzionamento \u00e8 sostanzialmente lo stesso del method overloading, ma con una differenza importante: mentre il method overloading permette di dichiarare effettivamente pi\u00f9 metodi con diversi body, nel function overload l\u2019effettiva funzione con il body deve essere una sola, ma possono essere specificate diverse signature per le varie versioni. Ci\u00f2 significa che la funzione dovr\u00e0 essere scritta in modo da essere compatibile con tutte le signature, altrimenti avremo un errore di tipo.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n Questo vincolo pu\u00f2 far sembrare il function overload limitante e di scarsa utilit\u00e0, ma bisogna tenere conto di un vantaggio importante: dichiarando la funzione in questo modo, TypeScript sar\u00e0 in grado di assegnare correttamente, a seconda dei parametri in ingresso, il tipo del valore che viene restituito. In altre parole, non dovremo preoccuparci di fare manualmente un ulteriore type narrowing su quello che ci restituisce la nostra funzione.<\/p>\n\n\n\n Per dimostrare le potenzialit\u00e0 di quanto detto, aggiungiamo un paio di righe allo snippet di prima.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n In questo esempio, trattiamo val1<\/strong> e val2<\/strong> rispettivamente come un numero e una stringa. Il motivo per cui possiamo farlo senza ottenere errori \u00e8 proprio il fatto che abbiamo utilizzato il function overload: TypeScript \u00e8 in grado di dire che someFunction<\/strong> restituir\u00e0 un numero quando riceve un numero e una stringa quando riceve una stringa.<\/p>\n\n\n\n Ecco come sarebbero cambiate le cose se non<\/em> avessimo usato il function overload.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n Gi\u00e0 questo semplicissimo esempio \u00e8 diventato molto pi\u00f9 verboso; potete immaginare l\u2019impatto che pu\u00f2 avere la cosa in un codice pi\u00f9 complesso e realistico.<\/p>\n\n\n\n Un\u2019applicazione molto valida per il function overload \u00e8 quando abbiamo una funzione che pu\u00f2 operare indifferentemente su un singolo valore o su un array di valori.<\/p>\n\n\n\n Provalo sul playground<\/a><\/p>\n\n\n\n (Notare fra l\u2019altro come anche Array.isArray<\/a> sia una type guard valida.)<\/p>\n\n\n\n Nessuna type guard necessaria al di fuori della funzione! Esistono forse parole pi\u00f9 dolci di \u201cposso scrivere meno codice\u201d?<\/p>\n\n\n\n Anche questo secondo articolo sulle funzionalit\u00e0 di TypeScript \u00e8 finito (non hai ancora letto la prima parte? Che aspetti?!<\/a>). Spero che questi paragrafi siano riusciti a farvi scoprire qualcosa di nuovo, o magari a farvi soffermare su qualche aspetto del linguaggio che non avevate mai considerato prima.<\/p>\n\n\n\n Di nuovo, se avete qualcuno dei vostri strumenti segreti per TypeScript che vi piacerebbe condividere, non vedo l\u2019ora di conoscerli. Uscir\u00e0 mai un terzo articolo? Chiss\u00e0!<\/p>\n\n\n\n Andrea Cioni.<\/a><\/p>\n\n\n\n Foto di Christopher Gower<\/a> su Unsplash<\/a>\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":" Lavorate nello sviluppo web? Vi siete mai trovati a lavorare su un progetto JavaScript relativamente complesso, come un server Node.js oppure un intricato portale React? Avete mai avuto difficolt\u00e0 dovute al linguaggio che vi hanno fatto perdere tempo e capelli per debuggare errori particolarmente subdoli? Beh, noi s\u00ec. Parecchie volte, in effetti. Abbastanza da farmi […]<\/p>\n","protected":false},"author":1,"featured_media":25737,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""}},"footnotes":""},"categories":[57],"tags":[],"class_list":["post-20582","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-sviluppo-software"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/posts\/20582","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/comments?post=20582"}],"version-history":[{"count":1,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/posts\/20582\/revisions"}],"predecessor-version":[{"id":24292,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/posts\/20582\/revisions\/24292"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/media\/25737"}],"wp:attachment":[{"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/media?parent=20582"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/categories?post=20582"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/odc.oimmei.dev\/it\/wp-json\/wp\/v2\/tags?post=20582"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}Tuple<\/h2>\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
const numberArray: number[] = [];\n\n\/\/ Validi:\nnumberArray.push(1);\nnumberArray.push(2);\n\n\/\/ Non valido:\nnumberArray.push(\"string\");<\/pre>\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\nUtility type: Readonly e NonNullable<\/h2>\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\ninterface 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\nGenerics: default, vincoli, condizioni<\/h2>\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\/\/ 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\/\/ 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\/\/ 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\n
\/\/ Multiple vincolato su boolean, non multiplo come default.\ninterface ComponentProps<T, Multiple extends boolean = false> {\n \/\/ Il valore \u00e8 un singolo elemento oppure un array.\n value: Multiple extends false ? T : T[]\n\n \/\/ onChange ha come parametro un singolo elemento oppure un array.\n onChange: (newValue: Multiple extends false ? T : T[]) => void\n}\n\n\/\/ Oggetto per il caso singolo (lascio il default su Multiple):\nconst singleValueComponentProps: ComponentProps<string> = {\n value: \"Sono una singola stringa!\",\n\n onChange: (newValue) => {\n console.log(\n \"Questo log stamper\u00e0 sempre TRUE:\", \n typeof newValue === \"string\"\n );\n },\n};\n\n\/\/ Oggetto per il caso multiplo:\nconst multipleValueComponentProps: ComponentProps<string, true> = {\n value: [\"Sono\", \"un\", \"array\", \"di\", \"stringhe\", \"ora!\"],\n\n onChange: (newValue) => {\n console.log(\n \"Posso usare i metodi di un array, perch\u00e9 newValue \u00e8 un array:\"\n );\n\n newValue.forEach((currentValue) => console.log(currentValue));\n },\n}<\/pre>\n\n\n\nType narrowing: type predicate<\/h2>\n\n\n\n
function someFunction (value: string | number): void {\n if (typeof value === \"string\") {\n \/\/ Stampo la lunghezza:\n console.log(\"Lunghezza della stringa:\", value.length);\n } else {\n \/\/ Stampo il valore:\n console.log(\"Valore:\", value.toFixed());\n }\n}<\/pre>\n\n\n\nfunction someFunction (value: string | number): void {\n \/\/ Stampo la lunghezza:\n console.log(\"Lunghezza della stringa:\", value.length);\n \/\/ Stampo il valore formattato:\n console.log(\"Valore:\", value.toFixed());\n}<\/pre>\n\n\n\ninterface SomeInterface {\n id: number\n\n title: string\n}\n\ninterface SomeExtension extends SomeInterface {\n description: string\n\n content: string\n\n otherFields: Record<string, any>\n}\n\nfunction someFunction (obj: SomeInterface): void {\n \/\/ Stampo tutte le propriet\u00e0.\n \/\/ Validi:\n console.log(obj.id);\n console.log(obj.title);\n\n if (\"description\" in obj) {\n \/\/ L'operatore in ci permette di accedere a description...\n console.log(obj.description);\n\n \/\/ ...ma non basta a far riconoscere obj \n \/\/ come oggetto di tipo SomeExtension.\n console.log(obj.content);\n console.log(obj.otherFields);\n }\n}<\/pre>\n\n\n\ninterface SomeInterface {\n id: number\n\n title: string\n}\n\ninterface SomeExtension extends SomeInterface {\n description: string\n\n content: string\n\n otherFields: Record<string, any>\n}\n\nfunction isSomeExtension (value: SomeInterface): value is SomeExtension {\n \/\/ Se value contiene description, \u00e8 di tipo SomeExtension.\n return \"description\" in value;\n}\n\nfunction someFunction (obj: SomeInterface): void {\n \/\/ Stampo tutte le propriet\u00e0.\n \/\/ Validi:\n console.log(obj.id);\n console.log(obj.title);\n\n if (isSomeExtension(obj)) {\n \/\/ Validi: obj \u00e8 di tipo SomeExtension in questo punto del codice.\n console.log(obj.description);\n console.log(obj.content);\n console.log(obj.otherFields);\n }\n}<\/pre>\n\n\n\nFunction overload<\/h2>\n\n\n\n
\/\/ Signature diverse:\nfunction someFunction (param: number): number;\nfunction someFunction (param: string): string;\n\n\/\/ Implementazione della funzione:\nfunction someFunction (\n param: number | string\n): number | string {\n if (typeof param === \"number\") {\n console.log(\"\u00c8 un numero!\");\n } else {\n console.log(\"\u00c8 una stringa!\");\n }\n\n return param;\n}\n\nconst val1 = someFunction(42);\nconst val2 = someFunction(\n \"Addio, e grazie per tutto il pesce\"\n);\n<\/pre>\n\n\n\n\/\/ Signature diverse:\nfunction someFunction (param: number): number;\nfunction someFunction (param: string): string;\n\n\/\/ Implementazione della funzione:\nfunction someFunction (\n param: number | string\n): number | string {\n if (typeof param === \"number\") {\n console.log(\"\u00c8 un numero!\");\n } else {\n console.log(\"\u00c8 una stringa!\");\n }\n\n return param;\n}\n\nconst val1 = someFunction(42);\nconst val2 = someFunction(\n \"Addio, e grazie per tutto il pesce\"\n);\n\n\/\/ Validi:\nconsole.log(val1.toFixed());\nconsole.log(val2.length);<\/pre>\n\n\n\nfunction someFunction (\n param: number | string\n): number | string {\n if (typeof param === \"number\") {\n console.log(\"\u00c8 un numero!\");\n } else {\n console.log(\"\u00c8 una stringa!\");\n }\n\n return param;\n}\n\nconst val1 = someFunction(42);\nconst val2 = someFunction(\n \"Addio, e grazie per tutto il pesce\"\n);\n\n\/\/ Non validi: TypeScript non \u00e8 in grado \n\/\/ di dedurre il valore delle variabili.\nconsole.log(val1.toFixed());\nconsole.log(val2.length);\n\n\/\/ Dobbiamo fare type narrowing manualmente:\nif (typeof val1 === \"number\") {\n console.log(val1.toFixed);\n}\nif (typeof val2 === \"string\") {\n console.log(val2.length);\n}<\/pre>\n\n\n\nfunction multiplyValue(\n value: number, \n multiplyBy: number,\n): number;\nfunction multiplyValue(\n value: number[], \n multiplyBy: number,\n): number[];\n\n\/\/ Moltiplica un valore o ogni valore\n\/\/ in un array per un operando.\nfunction multiplyValue(\n value: number | number[], \n multiplyBy: number,\n): number | number[] {\n if (Array.isArray(value)) {\n const result = [];\n\n for (let i = 0; i < value.length; i++) {\n result.push(value[i] * multiplyBy);\n }\n\n return result;\n } else {\n return value * multiplyBy;\n }\n}\n\nconst singleValue = multiplyValue(11, 2);\nconst arrayOfValues = multiplyValue(\n [1, 1, 2, 3, 5],\n 5\n);\n\n\/\/ singleValue \u00e8 un number:\nconsole.log(singleValue.toFixed());\n\n\/\/ arrayOfValues \u00e8 un array di number:\narrayOfValues.forEach((item) => {\n console.log(item.toFixed())\n});<\/pre>\n\n\n\nConclusione<\/h2>\n\n\n\n