En omfattende guide til “Module System” i TypeScript (med eksempler)
On oktober 16, 2021 by adminModule Standard
EcmaScript har standardiseret, hvordan vi skal importere moduler i JavaScript, og denne standard drejer sig om to nøgleord, import
og export
. Da modulsystemet i JavaScript bliver mere og mere populært, kommer der nogle nye ændringer til denne standard hvert år.
I denne vejledning vil vi se på semantikken i modulsystemet i TypeScript (som for det meste ligner EcmaScript-standarden), og hvordan import
og export
-nøgleordet fungerer.
Named Exports
Nøgleordet export
gør en værdi (variabel) defineret i en fil tilgængelig for import i andre modulfiler. Når et modul importeres i en anden modulfil ved hjælp af nøgleordet import
, kan den importerende fil angive de værdier, som den ønsker at få adgang til fra det importerede modul.
// program.ts
import { A } from 'path/to/values';console.log( A ); // "Apple"
I ovenstående eksempel importerer vi values.ts
i filen program.ts
og udtrækker det eksporterede A
-medlem. Det betyder, at values.ts
skal eksportere værdien A
i en eller anden form. Der er flere måder at eksponere A
på.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A, B, C, D };
Du kan eksportere enhver tilgængelig TypeScript-værdi med en gyldig identifikator (navn). Her i dette eksempel eksporterer vi en variabel A
(kunne også være en konstant), klasse B
, osv. Ved hjælp af export {}
-syntaksen kan du eksportere så mange værdier, som du vil, og alle kan importere dem ved hjælp af import { A, B }
-syntaksen. Du behøver ikke at importere alle de eksporterede værdier.
Der er en anden måde at eksportere en værdi på, hvor den blev deklareret.
// values.ts
export const A = { name: "Apple" };
export class B {}
export function C(){}
export enum D{}
I ovenstående eksempel har vi eksporteret alle værdierne, hvor de blev deklareret. Du behøver heller ikke at initialisere værdien, det kan du gøre senere nede i filen. Dette er en meget pænere måde i forhold til den foregående.
I begge tilfælde er alle dine eksportmedlemmer tilgængelige i samme fil, hvis du har brug for at bruge dem. At have export
nøgleordet på en værdi ændrer ikke dens adfærd inden for modulfilen.
Default Export
Så langt har vi eksporteret værdier, der kun kan importeres ved at angive navnet på eksportmedlemmer, såsom import { A } from 'path/to/values'
, hvor A
her er et eksportmedlem af values.ts
. Disse kaldes navngivne eksporter.
Du har også lov til at eksportere en enkelt værdi, som kan importeres uden at angive et navn. Denne værdi skal identificeres med nøgleordet default
sammen med nøgleordet export
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class A {}
function A(){}
enum A{}export default A;
Det er kun tilladt at eksportere en enkelt værdi som en standardeksport, og derfor har du ikke export default {...}
-udtryk at arbejde med. I modsætning til navngiven eksport kan en værdi ikke eksporteres som standard med en variabel- (eller konstant-) deklaration, bortset fra en function
og class
deklaration.
// values.ts
export default var A = "Apple"; // ❌ invalid syntax
export default enum D{} // ❌ illegal: not a function or class
export default class B {} // ✅ legal
export default function C(){} // ✅ legal
Det er dog muligt at eksportere en værdi direkte som en standardeksport. Det betyder, at ethvert JavaScript-udtryk kan eksporteres som standardeksportværdi.
// values.ts
export default "Hello"; // string
export default {}; // object
export default () => undefined; // function
export default function(){}; // function
export default class{}; // class
Da standardeksport mangler et eksportnavn, kan vi angive et hvilket som helst navn for den under import. Du kan have navngivne eksportværdier og en standardeksport i den samme modulfil. På samme måde kan du importere standardeksport- og navngivne eksportværdier i en enkelt import
-deklaration som vist nedenfor.
import Apple, { B, C } from 'path/to/values';
I ovenstående eksempel er Apple
det navn, vi brugte til at indsamle standardeksporten fra values.ts
. Du kan droppe { B, C }
-udtrykket eller Apple
-navnet fra import
-deklarationen, hvis du ikke vil bruge det.
💡 Ligesom ved navngivne eksporter er det ikke obligatorisk at importere standardeksportværdien, når
import
-deklarationen bruges. MenApple
skal komme før den navngivne eksport iimport
-deklarationens signatur.
Du kan også bruge as default
-syntaksen i export {}
-signaturen for at eksportere en værdi som standard sammen med andre navngivne eksportværdier. Dette kaldes aliasing, og det forklares i næste afsnit.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C, D };
💡 Grunden til, at jeg ikke bruger og anbefaler
default
eksport, har noget at gøre med udviklerens erfaring. Som vi lærte, kan du angive et hvilket som helst navn for et moduls standard-eksportmemeber iimport
-deklarationen. Hvis denne værdi importeres i flere filer, kan du referere til den med et vilkårligt navn efter eget valg, og det er en dårlig DX at have forskellige navne for det samme eksportmedlem i det samme modul.
Import And Export Alias
I tilfældet med standardeksport kan du referere til værdien med et vilkårligt navn efter eget valg i import
-deklarationen. Det er dog ikke tilfældet med navngivne eksporter. Du kan dog bruge nøgleordet as
til at referere til en navngiven eksportværdi med et navn efter eget valg.
// program.ts
import A, { B as Ball } from 'path/to/values';console.log( B ); // ❌ Error: Cannot find name 'B'.
console.log( Ball ); // ✅ legal
I ovenstående eksempel importerer vi B
fra values.ts
, men den er blevet omdøbt til Ball
. Derfor vil du i filen program.ts
bruge Apple
i stedet for B
i filen program.ts
. Når du giver et alias til et eksportmedlem, eksisterer det oprindelige navn ikke længere i det globale anvendelsesområde, og derfor vil B
ikke længere eksistere i program.ts
.
💡 Du kan ikke give alias til standard-eksportmedlemmet, da du allerede kan angive et vilkårligt navn efter eget valg, men du kan bruge
import { default as defaultValue, }
.
Du kan også aliasere en værdi, mens du eksporterer ved hjælp af nøgleordet as
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C as Cat, D };
I ovenstående eksempel har vi eksporteret variabel A
som standardeksport og funktion C
som Cat
. Derfor skal enhver, der importerer disse værdier fra values.ts
-filen, bruge standardimport-syntaksen for A
og Cat
for C
-værdien.
Importér alle navngivne eksporter
Du kan importere alle navngivne eksporter fra en fil ved hjælp af * as
-syntaksen.
// program.ts
import Apple, * as values from 'path/to/values';console.log( values.B );
console.log( values.C );
console.log( values.D );
Her vil alle de navngivne eksporter fra values.ts
blive gemt under values
, som vil være et objekt, hvor keys
vil være navnet på eksportmedlemmerne, og values
vil være de eksporterede værdier af eksportmedlemmerne.
Re-eksporterer
En importeret værdi kan reeksporteres på normal vis. Når du importerer noget, har du en reference til den importerede værdi, og du kan bruge den samme værdi i export
-syntaksen. Det er der ikke noget galt med.
// lib.ts
import A, { B, C as Cat, D } from 'path/to/values';export { D as default, A, B, Cat as C, D };
I ovenstående eksempel har du importeret et par værdier fra values.ts
inde i lib.ts
og eksporteret efter dine præferencer. Det er godt, hvis du vil bruge disse importerede værdier i lib.ts
samt eksportere nogle af dem, hvis en anden fil har brug for dem, når den importerer fra lib.ts
.
Vi har imidlertid også export ... from
-anvisningen til at eksportere værdier fra en fil uden at skulle importere dem først. Dette er godt, når du ønsker at have et enkelt indgangspunkt for alle importerne i dit modul.
// lib.ts// re-exports
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';// default export
export default "hello";class B {}
function C(){}// named exports
export { B, C as Cat };
I ovenstående eksempel eksporterer vi nogle af eksportmedlemmerne i values-1.ts
og values-2.ts
fra lib.ts
-filen (undtagen standardeksporten). Vi kan også bruge aliasing i export ... from
-syntaksen.
Reeksport-syntaksen påvirker ikke den aktuelle fil, og derfor kan du også have dine almindelige eksportmedlemmer i filen, som vist ovenfor. I modsætning til nøgleordet export
tillader export ... from
-syntaksen ikke adgang til reeksporterede medlemmer. Derfor vil du ikke kunne få adgang til P
eller Orange
i lib.ts
-filen.
// lib.ts
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';console.log( P ); // ❌ Error: Cannot find name 'P'.
console.log( Orange ); // ❌ Error: Cannot find name 'Orange'.
Du kan eksportere alle navngivne eksporter fra en fil ved hjælp af export * from
-syntaksen, så længe du har mulighed for at omdøbe dem, mens du eksporterer ved hjælp af export * as ... from
.
// lib.ts
export * from 'path/to/values-1';
export * as values2 from 'path/to/other/values-2';
Fra ovenstående eksempel, hvis values-1.ts
eksporterer P
og Q
, så kan alle importere P
og Q
fra lib.ts
. Men alle de navngivne eksportvarer fra values-2.ts
rep-eksporteres som values2
navngivne eksportvarer fra lib.ts
, og derfor skal du importere dem ved hjælp af følgende syntaks:
// program.ts
import { P, Q, values2 } from 'path/to/lib';
Du kan ikke få adgang til standardeksportvarer ved hjælp af export ... from
-syntaksen på normal vis. Men du kan eksportere standardeksporten ved hjælp af nøgleordet default
.
// lib.ts
export { default } from 'path/to/values-1';
export { default as Apple } from 'path/to/values-2';
I ovenstående eksempel er standardeksporten af lib.ts
standardeksportværdien af values-2.ts
. Standardeksportværdien af values-2
vil blive eksporteret fra lib.ts
som den Apple
navngivne eksport.
Import for sideeffekt
Kan du forestille dig at importere en fil uden at angive de eksporterede medlemmer?
import 'path/to/action';
I ovenstående eksempel importerer vi action.ts
, men vi har angivet alle de medlemmer, der eksporteres af action.ts
. Dette er en gyldig syntaks, men den bruges til at give et meget forskelligt resultat.
Når du importerer en fil ved hjælp af nøgleordet import
, udføres det først, og derefter er alle eksportmedlemmer tilgængelige for import. Derfor vil en fil, der importerer PI
, i nedenstående eksempel få 3.14
som et floating-point
-tal.
export const PI = parseFloat( "3.14" ); // 3.14
Følge samme logik, hvis du ønsker at skabe en sideeffekt ved at importere en fil, kan du helt sikkert gøre det. Så hvis der f.eks. er en fil, der initialiserer nogle globale variabler, kan du importere den uden at angive de eksporterede medlemmer af filen (den kunne også eksportere ingen i første omgang).
TypeScript-kompiler kan oprette en JavaScript-bundle-fil (.js
) ved at kompilere to eller flere .ts
-filer sammen. Dette gøres normalt ved at angive en entry TypeScript-fil og derefter gennemgå dens afhængighedstræ.
Et afhængighedstræ konstrueres ved at se på alle import
-deklarationer (afhængigheder) for entry-filen og følge afhængighederne for disse importerede filer. Hvis du ønsker at inkludere en fil, hvis kode kan skabe nogle sideeffekter ved kørselstid, skal du importere denne fil uden at angive de eksporterede medlemmer i import
-syntaksen. Dette kan gøres i indtastningsfilen eller i en hvilken som helst fil, der befinder sig inden for afhængighedstræet.
Skriv et svar