Importazione Esportazione Moduli

Edoardo Midali
Edoardo Midali

Il sistema di moduli in TypeScript, basato su ES6 modules, permette di organizzare codice in file separati e condividere funzionalità attraverso export e import. Ogni file TypeScript è un modulo con proprio scope, evitando inquinamento del namespace globale e permettendo riutilizzo controllato di codice attraverso esportazioni esplicite e importazioni selettive.

Export Base

Esportare elementi da un modulo per renderli disponibili ad altri file.

// export named
export const PI = 3.14159;
export let contatore = 0;

export function somma(a: number, b: number): number {
  return a + b;
}

export class Utente {
  constructor(public nome: string) {}
}

export interface Config {
  porta: number;
  host: string;
}

export type ID = string | number;

Export Default

Ogni modulo può avere un export default, ideale per esportazione principale.

// utils.ts
export default function calcolaArea(raggio: number): number {
  return Math.PI * raggio ** 2;
}

// Con classe
export default class Database {
  connect() {
    console.log("Connesso");
  }
}

// Con espressione
export default {
  nome: "App",
  versione: "1.0.0",
};

Import Named

Importare esportazioni named specifiche da altri moduli.

// Import singolo
import { somma } from "./math";

// Import multipli
import { somma, sottrai, moltiplica } from "./math";

// Import con alias
import { somma as add } from "./math";
import { Config as AppConfig } from "./types";

// Import tutto come namespace
import * as Math from "./math";
console.log(Math.somma(1, 2));

Import Default

Importare export default con nome arbitrario.

// Import default
import calcolaArea from "./utils";
import Database from "./database";
import config from "./config";

// Nome arbitrario
import qualsiasi from "./utils";

// Combinare default e named
import React, { useState, useEffect } from "react";
import Database, { connetti, disconnetti } from "./database";

Re-Export

Ri-esportare elementi da altri moduli per aggregazione.

// Re-export tutto
export * from "./utenti";
export * from "./prodotti";

// Re-export selettivo
export { Utente, creaUtente } from "./utenti";
export { Prodotto as Item } from "./prodotti";

// Re-export default come named
export { default as Database } from "./database";

// Aggregare in index.ts
export * from "./components/Button";
export * from "./components/Input";
export { default as Modal } from "./components/Modal";

Type-Only Import/Export

Importare/esportare solo tipi senza codice runtime.

// Export type
export type User = {
  id: number;
  nome: string;
};

export interface Config {
  porta: number;
}

// Import type-only
import type { User, Config } from "./types";

// Import misto
import { processaUtente, type User } from "./utenti";

// Export type-only
export type { User, Config };

Side Effects Import

Importare modulo per effetti collaterali senza binding.

// Esegue codice del modulo
import "./polyfills";
import "./styles.css";

// Modulo con side effects
// setup.ts
console.log("Inizializzazione globale");
globalThis.appVersion = "1.0.0";

Dynamic Import

Importare moduli dinamicamente a runtime con Promise.

// Dynamic import
async function caricaModulo() {
  const modulo = await import("./heavy-module");
  modulo.esegui();
}

// Con destructuring
async function usaUtility() {
  const { somma, moltiplica } = await import("./math");
  console.log(somma(1, 2));
}

// Conditional loading
async function caricaLocale(lingua: string) {
  if (lingua === "it") {
    const { messaggi } = await import("./locales/it");
    return messaggi;
  } else {
    const { messaggi } = await import("./locales/en");
    return messaggi;
  }
}

// Lazy loading componenti
const caricaComponente = () => import("./HeavyComponent");

Barrel Exports

Pattern per semplificare importazioni da directory con index file.

// components/index.ts
export { Button } from "./Button";
export { Input } from "./Input";
export { Modal } from "./Modal";
export * from "./Form";

// Uso
import { Button, Input, Modal } from "./components";

// Invece di
// import { Button } from './components/Button';
// import { Input } from './components/Input';
// import { Modal } from './components/Modal';

Module Resolution

TypeScript risolve moduli seguendo path relativi o configurati.

// Path relativo
import { Utente } from './types/utente';
import { Config } from '../config';

// Path assoluto (tsconfig paths)
import { Utente } from '@/types/utente';
import { api } from '@/services/api';

// Node modules
import express from 'express';
import { useState } from 'react';

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"]
    }
  }
}

Namespace (Legacy)

Namespace per organizzazione interna, meno comune con ES modules.

// Namespace definition
namespace Utilita {
  export function formatta(s: string): string {
    return s.toUpperCase();
  }

  export class Logger {
    log(msg: string) {
      console.log(msg);
    }
  }
}

// Uso
Utilita.formatta("hello");
const logger = new Utilita.Logger();

// Namespace annidati
namespace App {
  export namespace Models {
    export class Utente {}
  }
}

CommonJS Interop

Interoperabilità con moduli CommonJS (Node.js).

// Import CommonJS module
import express = require("express");

// Con esModuleInterop
import express from "express";

// Export per CommonJS
export = class Database {
  connect() {}
};

// Import namespace CommonJS
import * as fs from "fs";
fs.readFileSync("file.txt");

Circular Dependencies

Gestire dipendenze circolari tra moduli.

// a.ts
import { B } from "./b";

export class A {
  useB() {
    const b = new B();
  }
}

// b.ts
import type { A } from "./a"; // type-only per evitare circolare

export class B {
  useA(a: A) {
    // usa a
  }
}

// Alternativa: dependency injection
export class B {
  useA(a: any) {
    // runtime type check se necessario
  }
}

Declaration Files

File .d.ts per dichiarazioni tipo senza implementazione.

// types.d.ts
export interface Config {
  apiUrl: string;
  timeout: number;
}

export declare function initialize(config: Config): void;

// Ambient declarations
declare module "legacy-lib" {
  export function doSomething(): void;
}

// Global augmentation
declare global {
  interface Window {
    customProperty: string;
  }
}

Module Augmentation

Estendere moduli esistenti con nuove dichiarazioni.

// Augment existing module
import { User } from "./user";

declare module "./user" {
  interface User {
    nuovaProprietà: string;
  }
}

// Ora User ha nuovaProprietà
const user: User = {
  id: 1,
  nome: "Mario",
  nuovaProprietà: "valore",
};

// Augment external module
declare module "express" {
  interface Request {
    user?: User;
  }
}

Best Practices

Pattern e convenzioni per organizzazione moduli efficace.

// 1. Export preferito: named exports
export const costante = 42;
export function utilita() {}

// 2. Index per barrel exports
// index.ts
export * from "./modulo1";
export * from "./modulo2";

// 3. Type-only quando possibile
import type { Type1, Type2 } from "./types";

// 4. Raggruppare import
// External libraries
import React from "react";
import axios from "axios";

// Internal modules
import { Utente } from "@/models";
import { api } from "@/services";

// Types
import type { Config } from "@/types";

// 5. Evitare import *
// BAD
import * as tutto from "./modulo";

// GOOD
import { cosa, serve } from "./modulo";

Conclusioni

Il sistema di moduli in TypeScript fornisce meccanismo robusto per organizzare codice in unità riutilizzabili e manutenibili. Export e import permettono condivisione controllata di funzionalità, mentre features come dynamic import, type-only imports, e barrel exports offrono flessibilità per scenari diversi. Module resolution configurabile, interoperabilità CommonJS, e declaration files garantiscono compatibilità con ecosistema JavaScript esistente, risultando essenziale per applicazioni scalabili che richiedono separazione concerns, dependency management, e code splitting efficace.