10. Les modules
Les modules ECMAScript permettent de construire des applications structurées en fichiers indépendants et réutilisables, chacun exportant ce qu'il souhaite rendre accessible aux autres. Les scripts de ce chapitre se trouvent dans le dossier [modules] du projet.
10.1. scripts [import-01, export-01]
Le script [import-01] utilise le module [export-01] :
| // export par défaut d'un objet non nommé
export default {
data: 2,
do() {
console.log(this.data);
}
};
|
- [export default] exporte un objet non nommé — un seul export par défaut est possible par module.
| 'use strict';
// import d'un objet xporté par défaut
import export01 from './export-01.js';
// utilisation de cet objet
export01.do();
// on peut importer un export par défaut sous n'importe quel nom
import data from './export-01.js';
console.log(data.data);
|
- les lignes 3 et 7 importent l'export par défaut du module sous deux noms différents (export01, puis data) — le nom donné à l'import est libre, il n'a pas besoin de correspondre à quoi que ce soit dans le module source ;
- une fois importé, l'objet s'utilise comme s'il avait été défini localement.
npx tsx modules/import-01.ts
Résultat de l'exécution :
10.2. scripts [import-02, export-02]
Ces scripts montrent que l'export d'un objet nommé (stocké au préalable dans une variable) fonctionne exactement de la même façon :
| // export par défaut d'un objet nommé
const data = {
data: 2,
do() {
console.log(this.data);
}
};
// export
export default data;
|
| 'use strict';
// import d'un objet exporté par défaut
import module1 from './export-02.js';
// utilisation de cet objet
module1.do();
// on peut importer un export par défaut sous n'importe quel nom
import module2 from './export-02.js';
console.log(module2.data);
|
npx tsx modules/import-02.ts
Résultat de l'exécution :
10.3. scripts [import-03, export-03]
Un module peut exporter plusieurs éléments, avec la syntaxe export { élément } :
| // multi-exports
// export objet
const data = {
data: 2,
do() {
console.log(this.data);
}
};
// export fonction
export { data };
function doSomething(): void {
console.log("doSomething");
}
export { doSomething };
|
| 'use strict';
// import d'un module [export03]
import {data, doSomething} from './export-03.js';
// utilisation des imports
data.do();
doSomething();
// autre écriture
import * as module from './export-03.js';
// utilisation de l'import
console.log(module.data);
module.doSomething();
|
- ligne 3 : les imports nommés se font avec les noms exacts des éléments exportés, entre accolades ;
- ligne 8 : import * as module from '...' importe tous les éléments exportés, regroupés dans un objet nommé (ici module).
npx tsx modules/import-03.ts
Résultat de l'exécution :
| 2
doSomething
{ data: 2, do: [Function: do] }
doSomething
|
10.4. scripts [import-04, config-distante] — top-level await
[NOUVEAU depuis 2019] ECMAScript 2022 a introduit le top-level await : la possibilité d'utiliser await directement au premier niveau d'un module, sans l'entourer d'une fonction async. Avant cela, il fallait créer une fonction async « main » et l'appeler immédiatement (voir chapitre suivant, script async-06).
Le module [config-distante] simule le chargement d'une configuration distante, et utilise le top-level await :
| // module simulant la lecture d'une configuration distante (ex: appel réseau)
// utilisé par modules/import-04.js pour illustrer le top-level await
interface Configuration {
nomApplication: string;
version: string;
}
// une fonction qui simule un appel asynchrone (ex : fetch d'un fichier de configuration)
function chargerDepuisLeRéseau(): Promise<Configuration> {
return new Promise(resolve => {
setTimeout(() => {
resolve({ nomApplication: "cours ECMAScript", version: "2026" });
}, 300);
});
}
// [NOUVEAU depuis 2022] top-level await : on peut utiliser [await] directement
// au premier niveau d'un module, sans l'entourer d'une fonction async.
// le module ENTIER (et tout module qui l'importe) attend que cette ligne se termine
// avant de continuer : c'est très pratique pour initialiser une configuration
// une seule fois, au chargement du module.
console.log("[config-distante] chargement de la configuration en cours...");
const configuration: Configuration = await chargerDepuisLeRéseau();
console.log("[config-distante] configuration chargée");
// on exporte le résultat, déjà résolu (et non une promesse à re-attendre)
export default configuration;
|
Le script [import-04] importe ce module :
| 'use strict';
// ========================================================================
// [NOUVEAU depuis 2019] top-level await (ECMAScript 2022)
// ========================================================================
// avant ECMAScript 2022, il était impossible d'utiliser [await] en dehors
// d'une fonction [async] : au premier niveau d'un script, il fallait créer
// une fonction async "main" et l'appeler immédiatement (cf. async/async-06.js)
// remarque sur l'ordre d'exécution : avec les modules ES, les [import] sont toujours
// évalués AVANT le code du module qui importe, même écrits en 1ère ligne.
// c'est pourquoi les logs de [config-distante.js] s'affichent avant celui-ci,
// bien que la ligne suivante soit textuellement placée avant l'import dans ce fichier.
console.log("[import-04] avant l'import du module config-distante");
// grâce au top-level await utilisé DANS config-distante.js, l'import ci-dessous
// n'aboutit que lorsque la configuration a fini d'être chargée : pas besoin
// d'appeler .then() ni de créer de fonction async ici
import configuration from './config-distante.js';
console.log("[import-04] après l'import : la configuration est déjà disponible");
console.log("configuration =", configuration);
// on peut aussi utiliser [await] directement ici, dans ce module, au premier niveau
console.log("[import-04] attente de 200ms en top-level await...");
await new Promise(resolve => setTimeout(resolve, 200));
console.log("[import-04] terminé");
|
- ligne 19 : grâce au top-level await utilisé dans config-distante.ts, l'import de la ligne 19 n'aboutit que lorsque la configuration a fini d'être chargée — pas besoin de .then() ni de fonction async ici ;
- les modules ES sont toujours évalués avant le code du module qui les importe, même si l'instruction import n'est pas écrite en première ligne du fichier : c'est pourquoi les messages de config-distante.ts s'affichent avant celui d'import-04.ts, alors que ce dernier est textuellement placé avant l'import dans le fichier ;
- ligne 26 : on peut aussi utiliser await directement dans import-04.ts lui-même, à son propre premier niveau.
npx tsx modules/import-04.ts
Résultat de l'exécution :
| [config-distante] chargement de la configuration en cours...
[config-distante] configuration chargée
[import-04] avant l'import du module config-distante
[import-04] après l'import : la configuration est déjà disponible
configuration = { nomApplication: 'cours ECMAScript', version: '2026' }
[import-04] attente de 200ms en top-level await...
[import-04] terminé
|