CONTATTACI

Guide per aspiranti programmatori

sviluppatrice che programma seduta su una lampadina aiutata da un robottino
Lezione 28 / 30

Moduli e namespace in Typescript

Il primo livello di organizzazione architetturale del nostro codice è abbastanza integrato nelle moderne versioni di JavaScript; si tratta dei moduli. I moduli (o concetti ad essi analoghi) costituiscono la base di organizzazione del codice in praticamente tutti i linguaggi di programmazione

Lo scopo di un sistema di moduli è molteplice: 

  • dividere il codice in parti indipendenti tra loro 
  • dichiarare con esattezza i rapporti di dipendenza tra moduli 
  • ridurre la superficie di interazione tra moduli a specifici protocolli
  • limitare il grado di complessità e le competenze a un ambito ben circoscritto

Sin dai primi passi mossi nella programmazione informatica, praticamente ogni programmatore (per lo meno tra quelli ordinati) sente, ad esempio, l’esigenza di spezzare il codice in più files. In effetti, il sistema di moduli di JavaScript e, dunque, anche di TypeScript, individua in ogni file un modulo.

 

Che cos’è un modulo in Typescript?

Un modulo è, essenzialmente, un file TypeScript con del codice che si comporta come una funzione quando viene inizializzato e come un oggetto quando viene utilizzato. Tutto quello che scriviamo in un file che presenta delle istruzioni di import o export viene eseguito una volta sola alla sua inizializzazione e, dopodiché, viene esportato come un oggetto a disposizione di altri moduli. 

Immaginiamo di voler isolare il nostro contatore visto in precedenza in un modulo; creeremo un file counter.ts che contiene solo le logiche del contatore, escludendo l’interazione con il DOM

 

let _count = 0;

export function count(): number { return _count; }
export function increment(): void { _count++; }
export function reset(): void { _count = 0; }

Eccoci qui con un modulo semplicissimo che incapsula (cioè nasconde all’esterno) il suo stato nella variabile _count ed espone tre funzioni, una per leggerne il valore, le altre due per modificarlo secondo la logica di un contatore. 

Il codice che consuma il nostro modulo è ancora il nostro script principale che, però, è ora divenuto stateless, poiché non deve più prendersi cura di nessuna variabile:

 

import * as counter from "./counter";

const incrementBtn = document.querySelector("#increment") as HTMLButtonElement;
const resetBtn = document.querySelector("#reset") as HTMLButtonElement;
const counterSpan = document.querySelector("#counter") as HTMLSpanElement;

incrementBtn.addEventListener("click", () => {
  counter.increment();
  update();
});


resetBtn.addEventListener("click", () => {
  counter.reset();
  update();
});

function update(): void {
  counterSpan.innerText = String(counter.count());
}

In questo esempio non ci abbiamo guadagnato molto ad isolare lo stato in un modulo; ma, immaginiamo di voler combinare questa logica molto semplice con altre logiche: se ne starebbero ognuna confinata nel suo modulo, con il nostro script principale che si limita a fare da orchestratore. 

A dirla tutta, in praticamente tutti i framework esistenti, noi agiamo quasi esclusivamente a livello di moduli, estendendo le funzionalità del framework che, appunto, fa da orchestratore secondo le sue logiche di funzionamento. 

Per questo non dobbiamo preoccuparci particolarmente di capire esattamente come funziona internamente la gestione dei moduli, ma è utile capire bene come funzionano i meccanismi di importazione ed esportazione.

Tutto ciò che esportiamo usando export davanti a una definizione di variabili e funzioni (come negli esempi precedenti) può essere importato con le seguenti sintassi:

 

// importa tutto e lo mette in un oggetto 'counter'
import * as counter from "./counter";

// importa manualmente le singole exportazioni
import { count, increment, reset } from "./counter";

// importa singole esportazioni con un nome diverso
import { count as getCount } from "./counter";

Un modulo può anche esportare tipi: 

 

export interface Counter {
  count: number;
}

export type Count = number;

I tipi possono essere importati come fossero valori, oppure specificando che si tratta di tipi: 

 

// importa tutti i tipi di counter
import type * as counter from "./counter";
type CC = counter.Count;

// importa un tipo
import { type Counter } from "./counter";

L’uso di type per specificare i tipi non è obbligatorio di default, ma può essere richiesto in base alla configurazione di TypeScript in uso.

I moduli possono avere un’esportazione di default:

 

let _count = 0;

export function count(): number { return _count; }
export function increment(): void { _count++; }
export function reset(): void { _count = 0; }

export interface Counter {
  count: number;
}

export default {
  count,
  increment,
  reset
};

Le esportazioni di default si possono importare così: 

 

// importa un'esportazione di default
import counter from "./counter";

// importa il default più altre esportazioni
import counter, { type Count } from "./counter";

Namespace in Typescript

Per concludere, vediamo un costrutto che non ha un corrispettivo nel linguaggio JavaScript, che può avere qualche caso di utilizzo quando si vuole simulare il funzionamento di diversi moduli all’interno dello stesso file, oppure, al contrario, quando si vuole avere un modulo che attraversa più file; stiamo parlando dei namespace (o spazi di nomi):

 

namespace Counter {
  let _count = 0;

  export function count(): number { return _count; }
  export function increment(): void { _count++; }
  export function reset(): void { _count = 0; }

  export interface Counter {
    count: number;
  }

  export type Count = number;
}

type CC = Counter.Counter;
const { count } = Counter;

Counter si comporta come un oggetto in grado di contenere tipi ma anche variabili e funzioni, un po’ come se fosse un modulo nel file. 

I namespace sono molto potenti e versatili, ma concretamente hanno dei casi d’uso molto ristretti; per questo li menzioniamo senza approfondirli. 

A questo punto abbiamo tutti gli strumenti per suddividere il nostro codice in blocchetti autonomi che interagiscono tra loro attraverso esportazioni e importazioni. Ma c’è un limite a quello che abbiamo visto finora, che non abbiamo ancora affrontato: i moduli così definiti e utilizzati, sono statici, cioè esistono in un’unica copia attraverso tutta l’applicazione.

Il problema è che, nel mondo reale, potremmo aver bisogno di un modulo che si prenda cura di uno stato alla volta; abbiamo dunque bisogno di introdurre una forma di istanziamento dinamico dei moduli, e questo lo possiamo fare, essenzialmente, con due approcci: quello offerto dalla programmazione ad oggetti e quello offerto dalla programmazione funzionale. Nel primo caso, introdurremo le classi, nel secondo un costrutto che non introduce nulla di nuovo a livello sintattico ma che è altrettanto potente: le closures.

Contattaci senza impegno per informazioni sul corso

Pagamento rateale

Valore della rata: A PARTIRE DA 115 €/mese.

Esempio di finanziamento 

Importo finanziato: € 2440 in 24 rate da € 115 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 2841.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

*In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Pagamento rateale

Valore della rata: A PARTIRE DA 187 €/mese.

Esempio di finanziamento  

Importo finanziato: € 3990 in 24 rate da € 187 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 4572.88.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Contattaci senza impegno per informazioni sul corso

Scopriamo insieme se i nostri corsi fanno per te. Compila il form e aspetta la chiamata di uno dei nostri consulenti.