modularisering eller namnrymd i TypeScript


Jag vill att alla klasser ska vara tillgängliga under ett namnrymd i ett biblioteksbaserat projekt.

Jag saknar ett steg när det gäller att lägga alla klasser under ett namnrum.

Anta att jag har två klasser i två mappar

  1. Det första steget är att exportera alla under ett biblioteknamn eller ett namnrymde
  2. Det andra steget är att konsumera dem i en testmapp genom att komma åt klasser via deras biblioteksnamn eller namnrymd

Förväntan/Fråga: Att kunna få tillgång till och använda klasser genom ett gemensamt biblioteknamn, till exempel new mylib.Foo() eller new mylib.Bar()

Definition

src/a/Foo.ts

export module mylib {
    export class Foo {
    }
}

src/b/Bar.ts

export module mylib {
    export class Bar {
    }
}

src/index.ts

import { Foo } from './a/Foo'
import { Bar } from './b/Bar'

export { Foo, Bar }

Testning

test/a/Foo.spec.ts

import { mylib } from "../src";

test("constructor"), () = {
   const foo = new mylib.Foo() // expectation
}

test/b/Bar.spec.ts

test("constructor"), () = {
   const bar = new mylib.Bar() // expectation
}

Svar #1

Om du antar att du bara vill ha slutresultatet du frågade om och inte specifikt försöker använda TypeScripts module-syntax, kan du förenkla lite och sedan ha ett par alternativ.

Förenklingen är att du inte behöver dessa export module mylib { /* ... */ }-omslag i Foo.ts och Bar.ts, bara:

export class Foo
    // ...
}

och

export class Bar
    // ...
}

Då blir det en fråga om hur du vill ha dem importerade från index.ts.

Modulnamnrymdsobjekt

Det du har nu i index.ts är faktiskt helt okej för ett sätt det skulle kunna användas på: Det skapar två namngivna exporteringar, Foo och Bar. Om du vill skriva kod som använder dessa via ett mylib-objekt, kan du skapa det vid import:

import * as mylib from "../src";
//     ^^^^^^^^^^

Den där import-syntaksen säger "Importera modulens namnrymdsobjekt för modulen och tilldela det till en bunden variabel som heter mylib." Om modulens namnrymdsobjekt inte redan finns för den modulen kommer det att skapas.

Användningen när det har importerats är som visas i frågan, mylib.Foo osv.

Men om man gör det på det sättet är det också möjligt att använda import { Foo } from "../src"; och/eller import { Bar } from "../src";. Normalt sett är det en bra sak, men om du inte vill tillåta det, vill du inte göra individuella namngivna exports.

Observera att import * as mylib kommer att förstöra tree-shaking, om det är viktigt för det du gör. (Vilket är en del av varför import { Foo } och liknande normalt är en bra sak.) Men det är personen som använder ditt bibliotek som fattar det beslutet; ditt bibliotek hindrar inte tree-shaking av sig självt.

Exportera ett objekt

Om du inte vill använda modulsnamnrymdsobjektsmetoden ovan, kan du exportera ett objekt från index.ts:

export const mylib = { Foo, Bar };

...eller kanske exportera det fryst så att egenskaper inte kan läggas till, tas bort eller tilldelas om:

export const mylib = Object.freeze({ Foo, Bar });

Importera det då som du har visat i frågan:

import { mylib } from "../src";

Och användningen är mylib.Foo osv.

Var medveten om att detta motverkar tree-shaking, och inte bara baserat på hur användare använder det, utan på biblioteknivå (genom att inte ha Foo och Bar-exporter som folk kunde använda istället). Jag skulle inte rekommendera det.

Båda

Slutligen kunde du stödja både namngivna individuella exporteringar och en enda namngiven objektexport. I så fall skulle exporterna i index.ts se ut så här:

export { Foo, Bar };
export const mylib = Object.freeze({Foo, Bar});

Om du gör det lämnar du det åt personer som använder koden att välja om de vill använda import { Foo } eller import { mylib }. Det håller biblioteket vänligt för tree-shaking, samtidigt som det ger en namngiven export för hela biblioteket om det är användbart.