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
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 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 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 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 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 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 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 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 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 Provalo sul playground<\/strong><\/a><\/p>\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 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 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 Come modelliamo questa struttura dati nella nostra applicazione?<\/p>\n\n\n\n Pensando alla tipica tecnica object-oriented, potremmo utilizzare un\u2019interfaccia del genere, con status<\/strong> che pu\u00f2 assumere uno dei due valori e error_message<\/strong> sempre presente ma opzionale.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Lo svantaggio di questa interfaccia \u00e8 che non fornisce nessuna validazione statica su error_message<\/strong>. Siamo noi che dobbiamo assicurarci che il campo sia popolato nella giusta situazione, e possiamo accorgerci di eventuali errori solo in fase di debug.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Gli union type di TypeScript, tuttavia, ci mettono a disposizione una potente alternativa per risolvere il problema: la discriminated union<\/a>, che fa parte della pi\u00f9 ampia tematica del type narrowing<\/a>, ovvero la funzionalit\u00e0 che permette a TypeScript di capire il tipo di una certa variabile in base alla nostra gestione della tipizzazione e al flusso dell\u2019applicazione. Riscriviamo l\u2019esempio precedente in questo modo.<\/p>\n\n\n\n Ecco il risultato.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Con questa tipizzazione, il compilatore \u00e8 in grado di discriminare fra i due diversi tipi di risposta, e riuscir\u00e0 a validare staticamente la presenza e il tipo di error_message<\/strong>, sia in creazione che in lettura.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Rassicurante, non \u00e8 vero?<\/p>\n\n\n\n Per quanto siano comodi gli union type, hanno un difetto: essendo parte della tipizzazione statica, le informazioni sui possibili valori non possono in alcun modo essere utilizzate a runtime. Questo significa, per esempio, che non c\u2019\u00e8 modo di iterare sui possibili valori di uno union type, n\u00e9 di controllare se un certo valore ottenuto dinamicamente sia valido.<\/p>\n\n\n\n Il modo pi\u00f9 pulito per gestire situazioni in cui c\u2019\u00e8 bisogno di usare una enumerata di valori sia staticamente che a runtime \u00e8 – poco sorprendentemente – usare una Enum<\/a>. Le Enum sono tipi che permettono di definire una serie di costanti con un nome, vengono utilizzati dal compilatore per la tipizzazione statica e, a differenza degli union type, esistono anche a runtime.<\/p>\n\n\n\n Un risultato simile, per\u00f2, si pu\u00f2 ottenere anche con un array di scalari, come un array di stringhe. Per farlo, occorre definire l\u2019array con un costrutto chiamato const assertion<\/a>. Di che cosa si tratta?<\/p>\n\n\n\n Quando una variabile viene dichiarata e inizializzata senza un\u2019esplicita tipizzazione, TypeScript deduce per noi il tipo pi\u00f9 adeguato a seconda del valore con cui \u00e8 stata inizializzata. Un array di stringhe, quindi, ricever\u00e0 il tipo string[]<\/strong>.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Questo comportamento pu\u00f2 essere controllato con la const assertion, che impone a TypeScript di dedurre il tipo pi\u00f9 specifico possibile a partire dal valore. Un array di stringhe inizializzato con una serie di valori, di conseguenza, verr\u00e0 interpretato come un array in sola lettura, di lunghezza fissa, contenente solo quei valori in quelle specifiche posizioni – una tupla<\/a>, insomma.<\/p>\n\n\n\n L\u2019array pu\u00f2 comunque essere utilizzato in lettura nel codice, per iterare sui valori oppure controllare se un altro valore compare nella lista, ma con questo ulteriore accorgimento pu\u00f2 anche essere utilizzato per creare degli union type.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Questo ci permette di combinare i vantaggi dell\u2019avere tutti i valori che ci interessano in un array, molto utile per esempio nella validazione Yup<\/a>, e dell\u2019avere uno union type per controllare staticamente il valore di una propriet\u00e0 e avvalerci dell\u2019autocompletamento del nostro IDE.<\/p>\n\n\n\n Wow, ancora un altro trucco sugli union type! Quante possibilit\u00e0 c\u2019erano?<\/p>\n\n\n\n Come gi\u00e0 detto sopra, in molti casi gli union type sono utilizzati per creare variabili o propriet\u00e0 che possono assumere uno di una serie di valori scalari.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Questa funzionalit\u00e0 \u00e8 comoda, ma anche abbastanza limitante, dato che non possiamo introdurre nessuna variazione in questi valori. Tutto ci\u00f2 che non rientra precisamente negli scalari specificati nella definizione del tipo verr\u00e0 rifiutato dal compilatore senza tanti complimenti.<\/p>\n\n\n\n Ora, mettiamo di avere una situazione in cui una stringa pu\u00f2 assumere una serie di valori simili, ma non completamente statici. Per esempio, abbiamo un algoritmo che fa diversi tentativi per completare un task che potrebbe fallire, e vogliamo tenere una variabile che rappresenta lo stato di questo algoritmo.<\/p>\n\n\n\n Creiamo quindi una variabile status<\/strong>, che pu\u00f2 valere:<\/p>\n\n\n\n Per come siamo abituati a usare gli union type, il meglio che potremmo fare \u00e8 riportare i valori fissi e lasciare un generico string<\/em> per l\u2019ultimo\u2026<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n \u2026ma non \u00e8 l\u2019unica possibilit\u00e0. Le stringhe con cui formiamo gli union type possono infatti avere parti dinamiche, che possiamo gestire semplicemente concatenando il tipo della parte dinamica alla parte fissa con le template string<\/a>.<\/p>\n\n\n\n Provalo sul playground<\/strong><\/a><\/p>\n\n\n\n Vi vogliamo bene, union type <3<\/p>\n\n\n\n Approcciare TypeScript \u00e8 un\u2019esperienza stimolante per chi proviene da altri linguaggi di programmazione, soprattutto quelli orientati agli oggetti. La sua natura di estensione di un linguaggio non staticamente tipato come JavaScript ha permesso al team di sviluppo di trovare svariate soluzioni a problemi pi\u00f9 o meno comuni, e anche dopo diverso tempo capita di scoprirne qualcuna nuova di tanto in tanto.<\/p>\n\n\n\n Vi ho presentato alcune delle funzionalit\u00e0 che ho trovato pi\u00f9 interessanti nel corso del mio lavoro, e sono certo che ne scoprir\u00f2 ancora altre man mano che vado avanti.<\/p>\n\n\n\n Voi che ne pensate? Conoscevate tutti i trucchi presentati nell\u2019articolo, o sono riuscito a farvi scoprire qualcosa di nuovo? Ne conoscete altri che meritano di essere mostrati al mondo? Fatemelo sapere, magari un giorno o l\u2019altro ci sar\u00e0 un sequel!<\/p>\n\n\n\n <\/p>\n\n\n\nUtility type: Omit e Pick<\/h3>\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\ninterface 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\ninterface 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\/\/ 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\nAccedere al tipo di una propriet\u00e0<\/h3>\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\/\/ 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\/\/ 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\nType narrowing: discriminated union<\/h3>\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
\/\/ 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
\n
interface ApiResponse {\n status: \"SUCCESS\" | \"FAILURE\"\n\n data: any\n\n \/\/ Verr\u00e0 popolata solo in caso di status FAILURE.\n error_message?: string\n}<\/pre>\n\n\n\ninterface ApiResponse {\n status: \"SUCCESS\" | \"FAILURE\"\n\n data: any\n\n \/\/ Verr\u00e0 popolata solo in caso di status FAILURE.\n error_message?: string\n}\n\n\/\/ Valido: il compilatore non pu\u00f2 dedurre che questa situazione \u00e8 errata.\nconst response: ApiResponse = {\n status: \"SUCCESS\",\n data: {},\n error_message: \"Messaggio di errore\"\n}<\/pre>\n\n\n\n\n
interface ApiSuccessResponse {\n status: \"SUCCESS\"\n\n data: any\n}\n\ninterface ApiFailureResponse {\n status: \"FAIL\"\n\n data: any\n\n error_message: string\n}\n\ntype ApiResponse = ApiSuccessResponse | ApiFailureResponse;\n<\/pre>\n\n\n\ninterface ApiSuccessResponse {\n status: \"SUCCESS\"\n\n data: any\n}\n\ninterface ApiFailureResponse {\n status: \"FAIL\"\n\n data: any\n\n error_message: string\n}\n\ntype ApiResponse = ApiSuccessResponse | ApiFailureResponse;\n\n\/\/ Non valido: in caso di status SUCCESS non pu\u00f2 esserci error_message.\nconst response1: ApiResponse = {\n status: \"SUCCESS\",\n data: {},\n error_message: \"Messaggio di errore\",\n}\n\n\/\/ Non valido: in caso di status FAIL deve esserci error_message.\nconst response2: ApiResponse = {\n status: \"FAIL\",\n data: {},\n}\n\n\/\/ Validi:\nconst response3: ApiResponse = {\n status: \"SUCCESS\",\n data: {},\n}\nconst response4: ApiResponse = {\n status: \"FAIL\",\n data: {},\n error_message: \"Messaggio di errore\",\n}<\/pre>\n\n\n\nConst assertion (array di scalari come union type)<\/h3>\n\n\n\n
const array = [\"stringa_1\", \"stringa_2\", \"stringa_3\"];\n\n\/\/ Tipo dedotto:\n\/\/ declare const array: string[];<\/pre>\n\n\n\n
const array = [\"stringa_1\", \"stringa_2\", \"stringa_3\"] as const;\n\n\/\/ Tipo dedotto:\n\/\/ declare const array: readonly [\"stringa_1\", \"stringa_2\", \"stringa_3\"]<\/pre>\n\n\n\n
const someArray = [\"stringa_1\", \"stringa_2\", \"stringa_3\"] as const;\n\ntype SomeUnion = typeof someArray[number];\n\n\/\/ Valido:\nconst someVar1: SomeUnion = \"stringa_1\";\n\n\/\/ Non valido:\nconst someVar2: SomeUnion = \"stringa_5\";\n\n\/\/ Posso comunque usare someArray come array:\nif (someArray.indexOf(\"stringa_1\") !== -1) {\n console.log(\"Tipo valido\");\n}\nfor (let i = 0; i < someArray.length; i++) {\n console.log(someArray[i]);\n}<\/pre>\n\n\n\nTipi union dinamici<\/h3>\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
\n
\/\/ Union type per lo status:\ntype StatusType = \"none\" | \"success\" | \"fail\" | string;\n\n\/\/ Questo \u00e8 valido...\nconst status1: StatusType = \"none\";\nconst status2: StatusType = \"attempt_3\";\n\n\/\/ ...ma anche questo \u00e8 valido.\nconst status3: StatusType = \"Ehi, ma questa stringa non contiene uno stato!\";<\/pre>\n\n\n\n
\/\/ Union type per lo status:\ntype StatusType = \"none\" | \"success\" | \"fail\" | `attempt_${number}`;\n\n\/\/ Questi sono validi...\nconst status1: StatusType = \"none\";\nconst status2: StatusType = \"attempt_3\";\n\n\/\/ ...e questo no.\nconst status3: StatusType = \"Oh :(\";<\/pre>\n\n\n\nConclusione<\/h4>\n\n\n\n