Un guide complet du « Système de modules » en TypeScript (avec des exemples)
On octobre 16, 2021 by adminNorme de modules
EcmaScript a normalisé la façon dont nous devrions importer des modules en JavaScript et cette norme tourne autour de deux mots-clés, import
et export
. Comme le système de modules en JavaScript devient populaire, il y a quelques nouveaux changements qui viennent à cette norme chaque année.
Dans ce tutoriel, nous allons examiner la sémantique du système de modules dans TypeScript (qui ressemble principalement à la norme EcmaScript) et comment le mot-clé import
et export
fonctionne.
Exportations nommées
Le mot-clé export
rend une valeur (variable) définie dans un fichier disponible pour l’importation dans d’autres fichiers de modules. Lorsqu’un module est importé dans un autre fichier de module à l’aide du mot-clé import
, le fichier importateur peut spécifier les valeurs auxquelles il veut accéder à partir du module importé.
// program.ts
import { A } from 'path/to/values';console.log( A ); // "Apple"
Dans l’exemple ci-dessus, nous importons values.ts
dans le fichier program.ts
et extrayons le membre exporté A
. Cela signifie que values.ts
doit exporter la valeur A
sous une forme ou une autre. Il existe plusieurs façons d’exposer A
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A, B, C, D };
Vous pouvez exporter toute valeur TypeScript accessible avec un identifiant (nom) valide. Ici, dans cet exemple, nous exportons une variable A
(pourrait également être une constante), la classe B
, et ainsi de suite. En utilisant la syntaxe export {}
, vous pouvez exporter autant de valeurs que vous le souhaitez et n’importe qui peut les importer en utilisant la syntaxe import { A, B }
. Vous n’avez pas besoin d’importer toutes les valeurs exportées.
Il existe une autre façon d’exporter une valeur là où elle a été déclarée.
// values.ts
export const A = { name: "Apple" };
export class B {}
export function C(){}
export enum D{}
Dans l’exemple ci-dessus, nous avons exporté toutes les valeurs là où elles ont été déclarées. Vous n’avez pas non plus besoin d’initialiser la valeur, vous pouvez le faire plus tard dans le fichier. C’est une façon beaucoup plus agréable par rapport à la précédente.
Dans les deux cas, tous vos membres d’exportation sont accessibles dans le même fichier si vous avez besoin de les utiliser. Avoir le mot-clé export
sur une valeur ne change pas son comportement au sein du fichier du module.
Exportation par défaut
Jusqu’à présent, nous avons exporté des valeurs qui ne peuvent être importées qu’en fournissant le nom des membres d’exportation comme import { A } from 'path/to/values'
où A
ici est un membre d’exportation de values.ts
. Ce sont des exportations nommées.
Vous êtes également autorisé à exporter une seule valeur qui peut être importée sans spécifier de nom. Cette valeur doit être identifiée avec le mot-clé default
en même temps que le mot-clé export
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class A {}
function A(){}
enum A{}export default A;
Seule une valeur unique est autorisée à être exportée en tant qu’exportation par défaut, donc vous n’avez pas d’expression export default {...}
à travailler. Contrairement aux exportations nommées, une valeur ne peut pas être exportée par défaut avec une déclaration de variable (ou de constante), à l’exception d’une déclaration function
et class
.
// 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
Cependant, vous pouvez exporter une valeur directement comme exportation par défaut. Cela signifie que n’importe quelle expression JavaScript peut être exportée comme valeur d’exportation par défaut.
// values.ts
export default "Hello"; // string
export default {}; // object
export default () => undefined; // function
export default function(){}; // function
export default class{}; // class
Puisque l’exportation par défaut est dépourvue de nom d’exportation, nous pouvons lui fournir n’importe quel nom lors de l’importation. Vous pouvez avoir des exportations nommées et une exportation par défaut dans le même fichier de module. De même, vous pouvez importer les valeurs d’exportation par défaut et d’exportation nommée dans une seule déclaration import
comme indiqué ci-dessous.
import Apple, { B, C } from 'path/to/values';
Dans l’exemple ci-dessus, le Apple
est le nom que nous avons utilisé pour recueillir l’exportation par défaut à partir du values.ts
. Vous pouvez laisser tomber l’expression { B, C }
ou le nom Apple
de la déclaration import
si vous ne comptez pas l’utiliser.
💡 Comme les exportations nommées, il n’est pas obligatoire d’importer la valeur d’exportation par défaut lorsque la déclaration
import
est utilisée. Mais, leApple
doit venir avant les exportations nommées dans la signature de la déclarationimport
.
Vous pouvez également utiliser la syntaxe as default
dans la signature export {}
pour exporter une valeur par défaut avec d’autres exportations nommées. Ceci est appelé aliasing et il est expliqué dans la section suivante.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C, D };
💡 La raison pour laquelle je n’utilise pas et ne recommande pas l’exportation
default
a à voir avec l’expérience du développeur. Comme nous l’avons appris, vous pouvez fournir n’importe quel nom pour le membre d’exportation par défaut d’un module dans la déclarationimport
. Si cette valeur est importée dans plusieurs fichiers, vous pouvez la référencer avec n’importe quel nom de votre choix et avoir différents noms pour le même membre d’exportation du même module est un mauvais DX.
Importer et exporter des alias
Dans le cas de l’exportation par défaut, vous pouvez référencer la valeur avec n’importe quel nom de votre choix dans la déclaration import
. Cependant, ce n’est pas le cas avec les exportations nommées. Cependant, vous pouvez utiliser le mot-clé as
pour référencer une valeur d’exportation nommée avec le nom de votre choix.
// program.ts
import A, { B as Ball } from 'path/to/values';console.log( B ); // ❌ Error: Cannot find name 'B'.
console.log( Ball ); // ✅ legal
Dans l’exemple ci-dessus, nous importons B
du values.ts
mais il a été renommé en Ball
. Par conséquent, dans le fichier program.ts
, vous utiliserez Apple
au lieu de B
. Lorsque vous aliassez un membre d’exportation, le nom original n’existe plus dans la portée globale, donc B
n’existera plus dans le program.ts
.
💡 Vous ne pouvez pas aliaser le membre d’exportation par défaut puisque vous pouvez déjà fournir n’importe quel nom de votre choix mais vous pouvez utiliser
import { default as defaultValue, }
.
Vous êtes également autorisé à aliaser une valeur lors de l’exportation en utilisant le mot-clé 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 };
Dans l’exemple ci-dessus, nous avons exporté la variable A
comme export par défaut et la fonction C
comme Cat
. Par conséquent, toute personne qui importe ces valeurs à partir du fichier values.ts
doit utiliser la syntaxe d’importation par défaut pour A
et Cat
pour la valeur C
.
Importer toutes les exportations nommées
Vous pouvez importer toutes les exportations nommées à partir d’un fichier en utilisant la syntaxe * as
.
// program.ts
import Apple, * as values from 'path/to/values';console.log( values.B );
console.log( values.C );
console.log( values.D );
Ici, toutes les exportations nommées de values.ts
seront enregistrées sous values
qui sera un objet où le keys
sera le nom des membres de l’exportation et values
sera les valeurs exportées des membres de l’exportation.
Réexportations
Une valeur importée peut être réexportée de façon normale. Lorsque vous importez quelque chose, vous avez une référence à la valeur d’importation et vous pouvez utiliser la même valeur dans la syntaxe export
. Rien de mal à cela.
// lib.ts
import A, { B, C as Cat, D } from 'path/to/values';export { D as default, A, B, Cat as C, D };
Dans l’exemple ci-dessus, vous avez importé quelques valeurs de values.ts
à l’intérieur de lib.ts
et exporté selon votre préférence. C’est génial si vous voulez utiliser ces importations dans lib.ts
ainsi qu’exporter certaines d’entre elles si un autre fichier en a besoin lorsqu’il importe depuis le lib.ts
.
Cependant, nous avons aussi l’instruction export ... from
pour exporter les valeurs d’un fichier sans avoir à les importer d’abord. C’est génial lorsque vous voulez garder un seul point d’entrée pour toutes les importations dans votre module.
// 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 };
Dans l’exemple ci-dessus, nous exportons certains des membres d’exportation de values-1.ts
et values-2.ts
à partir du fichier lib.ts
(sauf l’exportation par défaut). Nous pouvons également utiliser l’aliasing dans la syntaxe export ... from
.
La syntaxe de réexportation n’affecte pas le fichier actuel, donc vous pouvez avoir vos exportations régulières dans le fichier ainsi que montré ci-dessus. Contrairement au mot-clé export
, la syntaxe export ... from
ne permet pas l’accès aux membres réexportés. Par conséquent, vous ne serez pas en mesure d’accéder à P
ou Orange
dans le fichier lib.ts
.
// 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'.
Vous pouvez exporter toutes les exportations nommées d’un fichier en utilisant la syntaxe export * from
, car vous avez la possibilité de les renommer pendant l’exportation en utilisant export * as ... from
.
// lib.ts
export * from 'path/to/values-1';
export * as values2 from 'path/to/other/values-2';
D’après l’exemple ci-dessus, si values-1.ts
exporte P
et Q
, alors n’importe qui peut importer P
et Q
de lib.ts
. Cependant, toutes les exportations nommées de values-2.ts
sont réexportées en tant qu’exportations nommées values2
de lib.ts
, donc vous devez les importer en utilisant la syntaxe suivante.
// program.ts
import { P, Q, values2 } from 'path/to/lib';
Vous ne pouvez pas accéder à l’exportation par défaut en utilisant la syntaxe export ... from
de manière normale. Mais vous pouvez exporter l’exportation par défaut en utilisant le mot-clé default
.
// lib.ts
export { default } from 'path/to/values-1';
export { default as Apple } from 'path/to/values-2';
Dans l’exemple ci-dessus, l’exportation par défaut de lib.ts
est la valeur d’exportation par défaut de values-2.ts
. La valeur d’exportation par défaut de values-2
sera exportée de lib.ts
en tant que Apple
nommée exportation.
Importation pour effet de bord
Pouvez-vous imaginer importer un fichier sans spécifier les membres exportés ?
import 'path/to/action';
Dans l’exemple ci-dessus, nous importons action.ts
mais nous avons spécifié tous les membres exportés par le action.ts
. C’est une syntaxe valide mais elle est utilisée pour produire un résultat très différent.
Lorsque vous importez un fichier en utilisant le mot clé import
, il est d’abord exécuté et ensuite tous les membres exportés sont disponibles pour l’importation. Par conséquent, dans l’exemple ci-dessous, un fichier important PI
obtiendrait le 3.14
comme un nombre floating-point
.
export const PI = parseFloat( "3.14" ); // 3.14
Suivant la même logique, si vous voulez créer un effet secondaire en important un fichier, alors vous pouvez totalement le faire. Ainsi, par exemple, s’il y a un fichier qui initialise certaines variables globales, vous pouvez l’importer sans spécifier les membres exportés de ce fichier (il pourrait également ne pas en exporter en premier lieu).
Le compilateur TypeScript peut créer un fichier bundle JavaScript (.js
) en compilant deux ou plusieurs fichiers .ts
ensemble. Cela se fait normalement en fournissant un fichier TypeScript d’entrée et en parcourant ensuite son arbre de dépendance.
Un arbre de dépendance est construit en regardant toutes les déclarations import
(dépendances) du fichier d’entrée et en suivant les dépendances de ces fichiers importés. Si vous voulez inclure un fichier dont le code pourrait créer certains effets secondaires à l’exécution, alors vous devez importer ce fichier sans spécifier les membres exportés dans la syntaxe import
. Cela peut être fait dans le fichier d’entrée ou à l’intérieur de n’importe quel fichier qui se trouve dans l’arbre de dépendance.
Laisser un commentaire