web-dev-qa-db-fra.com

Module vs Namespace - Import vs Require Typescript

Je reçois beaucoup de confusion avec module/namespace/export et import, require, reference utilisation. Être de Java de fond, quelqu'un peut-il m'expliquer en résumé quand utiliser quoi et quel est le bon design? Je sens que je me trompe quand j'écris un exemple de projet

Jusqu'ici, je comprends 1. module pour les packages externes 2. namespace pour les packages internes

  • Je n'ai pas compris comment nous les catégorisons?
  • Quand exporter une classe, un espace de noms ou un package?
  • Si nous exportons package/namespace, toutes les classes qui le composent sont exportées ou doivent l'être explicitement
  • Comment chacun d'entre eux peut être importé/requis?

Selon doc , si je crée chaque fichier "ts" pour chaque gestionnaire/modèle, TypeScript ne recommande pas d'utiliser des "espaces de noms"? Utiliser directement les chemins de référence?

S'il vous plaît expliquer en détail que je viens de fond différent et pas sûr de ES6/ES5 etc.

J'ai vu plusieurs personnes soulever/se confondre avec les mêmes questions. J'espère que quelqu'un pourra expliquer en détail le scénario du monde réel

74
Reddy

Je n'ai pas compris comment nous les catégorisons?

Les espaces de noms sont utilisés pour organiser/encapsuler votre code. Les modules externes sont utilisés pour organiser/encapsuler votre code ET pour localiser votre code au moment de l'exécution. En pratique, vous avez deux choix au moment de l'exécution: 1) combinez tous les codes transpilés dans un seul fichier, ou 2) utilisez des modules externes et disposez de plusieurs fichiers et nécessitez un autre mécanisme pour accéder à ces fichiers.

Quand exporter une classe, un espace de noms ou un package?

Pour rendre un type ou une valeur visible en dehors du fichier dans lequel il se trouve, vous devez l'exporter s'il se trouve à l'intérieur d'un espace de noms. Que vous l'exportiez au niveau supérieur ou dans un espace de noms, vous décidez s'il se trouve maintenant dans un module externe.

Si nous exportons package/namespace, toutes les classes qui le composent sont exportées ou doivent l'être explicitement

Les classes d'un espace de noms devront toujours être explicitement exportées pour que la classe soit visible au moment de la compilation en dehors du fichier dans lequel elle est définie.

Comment chacun d'entre eux peut être importé/requis?

Cela dépend si vous utilisez des modules externes. Un module externe devra toujours être importé pour "l'utiliser". Importer un espace de noms qui ne se trouve pas dans un module externe ne fournit en réalité qu'un alias pour cet espace de noms. Vous devez toujours préfixer le type/what avec l'alias (et c'est pourquoi vous ne souhaitez généralement pas utiliser d'espaces de noms avec des modules externes. cela signifie que vous devez toujours utiliser un préfixe lorsque vous référencez un élément fourni par le module externe.) Les espaces de noms qui ne font pas partie d'un module externe peuvent s'étendre sur plusieurs fichiers. Ainsi, si vous êtes dans le même espace de noms, vous pouvez vous référer à tout élément exporté par le espace de noms sans avoir besoin d'aucune sorte d'importation.

Pour bien comprendre ce qui précède, vous avez besoin de connaissances de base. L’essentiel pour comprendre les références/espaces de noms/modules externes est ce que ces constructions font lors de la compilation et ce qu’elles font lors de l’exécution.

Les directives de référence sont utilisées lors de la compilation pour localiser les informations de type. Votre source contient un symbole particulier. Comment le compilateur TypeScript localise-t-il la définition de ce symbole? La directive reference a été en grande partie intégrée au mécanisme tsconfig.json - en utilisant tsconfig.json, vous indiquez au compilateur où se trouvent toutes vos sources.

Les espaces de noms peuvent contenir des définitions de types et/ou des implémentations. Si un espace de noms ne contient que des informations de type, il n'a aucune manifestation d'exécution. Vous pouvez le vérifier en consultant la sortie JS et en recherchant un fichier JS vide. Si un espace de noms comporte un code d'implémentation, celui-ci est encapsulé dans une fermeture affectée à une variable globale portant le même nom que l'espace de noms. Avec les espaces de noms imbriqués, il y aura une variable globale pour l’espace de noms racine. Encore une fois, vérifiez la sortie JS. Historiquement, les espaces de noms représentent la façon dont les bibliothèques côté client JS ont tenté d’éviter le problème des collisions de nommage. L'idée est de regrouper toute votre bibliothèque dans une fermeture et d'exposer ensuite une empreinte globale aussi petite que possible - une seule variable globale référençant la fermeture. Eh bien, le problème est toujours que vous avez revendiqué un nom dans l'espace global. Et si vous vouliez, par exemple, deux versions d'une bibliothèque? Un espace de noms TypeScript pose toujours le problème de la localisation de la source pour cet espace de noms. C'est-à-dire que le code source qui référence A.B a toujours le problème de dire au compilateur comment localiser A.B - en utilisant des directives de référence ou en utilisant tsconfig.json. Ou en plaçant l'espace de noms dans un module externe, puis en important le module externe.

Les modules externes ont été créés avec JS côté serveur. Il existe une correspondance un à un entre un module externe et un fichier sur le système de fichiers. Vous pouvez utiliser la structure de répertoires du système de fichiers pour organiser les modules externes dans une structure imbriquée. L'importation d'un module externe introduit généralement une dépendance d'exécution à ce module externe (sauf lorsque vous importez un module externe mais n'utilisez aucune de ses exportations dans la position valeur. En d'autres termes, vous importez uniquement le module externe. pour obtenir ses informations de type). Un module externe est implicitement dans une fermeture, ce qui est essentiel: l'utilisateur du module peut affecter la fermeture à la variable locale de son choix. TypeScript/ES6 ajoute une syntaxe supplémentaire autour du mappage des exportations des modules externes sur des noms locaux, mais cela reste une bonne chose. Du côté du serveur, la localisation d’un module externe est relativement simple: il suffit de localiser le fichier représentant le module externe sur le système de fichiers local. Si vous souhaitez utiliser des modules externes côté client, dans un navigateur, cela devient plus complexe car il n’ya pas d’équivalent au système de fichiers qui dispose du module pour le chargement. Alors maintenant, côté client, vous avez besoin d’un moyen de regrouper tous ces fichiers dans un formulaire qui peut être utilisé à distance dans le navigateur - c’est là que les groupeurs de modules comme Webpack (Webpack en fait beaucoup plus que les modules de groupement) et Browserify entre en jeu. Les bundlers de modules permettent la résolution à l'exécution de vos modules externes dans le navigateur.

Scénario du monde réel: AngularJS. Les modules externes fictifs n'existent pas, utilisez un seul espace de noms pour limiter la pollution de l'espace global (dans l'exemple ci-dessous, une seule variable correspond uniquement à MyApp), exportez uniquement des interfaces et utilisez l'injection de dépendance AngularJS pour effectuer des implémentations. disponible pour utilisation. Placez toutes les classes dans une racine de répertoire, ajoutez un tsconfig.json à la racine, installez les typings angularjs sous la même racine de répertoire de sorte que tsconfig.json le récupère également, combinez toutes les sorties dans un fichier JS. Cela fonctionnera très bien pour la plupart des projets si la réutilisation du code ne pose pas de problème.

MyService.ts:

namespace MyApp {

    // without an export the interface is not visible outside of MyService.ts
    export interface MyService { 
        ....
    }

    // class is not exported; AngularJS DI will wire up the implementation
    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
}

MyController.ts:

namespace MyApp {

   class MyController {
       // No import of MyService is needed as we are spanning 
       // one namespace with multiple files.
       // MyService is only used at compile time for type checking. 
       // AngularJS DI is done on the name of the variable. 
       constructor(private myService: MyService) { 
       }
   }
   angular.module("MyApp").controller("myController", MyController);
}

Utilisation de IIFE pour éviter de polluer la portée globale de l'exécution. Dans cet exemple, aucune variable globale n'est créée. (Un tsconfig.json est supposé.)

Foo.ts:

namespace Foo {
    // without an export IFoo is not visible. No JS is generated here
    // as we are only defining a type.
    export interface IFoo {
        x: string;
    }
}

interface ITopLevel {
    z: string;
}

(function(){
    // export required above to make IFoo visible as we are not in the Foo namespace
    class Foo1 implements Foo.IFoo {
        x: string = "abc";
    }
    // do something with Foo1 like register it with a DI system
})();

Bar.ts:

// alias import; no external module created
import IFoo = Foo.IFoo;

(function() {

    // Namespace Foo is always visible as it was defined at
    // top level (outside of any other namespace).
    class Bar1 implements Foo.IFoo {
        x: string;
    }

    // equivalent to above
    class Bar2 implements IFoo {
        x: string;
    }

    // IToplevel is visible here for the same reason namespace Foo is visible
    class MyToplevel implements ITopLevel {
        z: string;
    }

})();

En utilisant IIFE, vous pouvez éviter d’introduire MyApp en tant que variable globale dans le premier exemple.

MyService.ts:

interface MyService { 
    ....
}

(function() {

    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
})();

MyController.ts:

(function() { 

   class MyController { 
       constructor(private myService: MyService) { 
       }
   }

   angular.module("MyApp").controller("myController", MyController);
})();
61
kayjtea

Il y a deux choses:

  • Un module dans TypeScript est une notion standard de l'ES6, il utilise import/export mots-clés au niveau supérieur du code;
  • Un espace de noms est une notion spécifique à TypeScript permettant de structurer le code de manière obsolète.

Espaces de noms

C'est presque une notion obsolète. Avant les modules ES6, la méthode courante pour séparer le code JavaScript dans un navigateur consistait à créer des variables globales. Par exemple, toutes les fonctions d’une API telles que le trait de soulignement étaient situées dans une variable globale appelée _.

Cette ancienne façon de procéder est comme Java packages ou PHP espaces de noms. Il n'est pas adapté au Web. Le nouveau standard ECMAScript résout des problèmes tels que: comment utiliser Comment utiliser deux versions distinctes de la même bibliothèque?

Remarque: dans les versions de TypeScript antérieures à la définition de "modules" d'ECMAScript (été 2014), les espaces de noms étaient appelés "modules internes" et les modules, "modules externes".

Remarque 2: le mot clé export à l'intérieur d'un namespace est une utilisation non standard de TypeScript du mot clé. C'est le moyen de déclarer une chose qui est publiquement accessible de l'extérieur du namespace.

Modules ES6

Un module est un fichier contenant les mots-clés import ou export au plus haut niveau du code.

TypeScript suit la norme d'ECMAScript. Je suggère de lire ne bonne introduction aux modules ES6 dans un article de Mozilla.

Si vous souhaitez utiliser des modules dans une application frontale (dans un navigateur), vous devrez utiliser un bundle ( Webpack [ la documentation ici ], Browserify) ou un chargeur ( SystemJS [- n tutoriel ici ], RequireJS) et configurer TypeScript avec cet environnement.

Si votre code est exécuté dans Node.js, configurez simplement le compilateur TypeScript pour générer le format CommonJS.

Remarque: Un espace de noms peut être déclaré dans un module. Dans ce cas, il ne sera pas accessible en tant que variable globale de l'extérieur du module. Cependant, il peut être exporté à partir du module.

21
Paleo
  1. module est pour les paquets externes 2. namespace est pour les paquets internes

En fait, le mot clé module a été remplacé par le mot clé namespace.

Une meilleure déclaration est donc Les modules sont ce qu'on appelait auparavant des modules externes, l'espace de noms est ce qu'on appelait les modules internes.

Plus

J'espère que cela vous aidera davantage: https://basarat.gitbooks.io/TypeScript/content/docs/project/modules.html

13
basarat

"requis" et "importation" ont une fonctionnalité équivalente. nous pouvons les utiliser de manière interchangeable parce que nous avons des transpilers qui ne se soucient pas vraiment de savoir si le navigateur les prend en charge nativement ou non. mais, alors que "require" a ses racines dans un ancien style de codage provenant de CommonJS jusqu'en 2009, "import" dérive sa syntaxe de la syntaxe largement acceptée ES6 (ES2015). vous devez donc utiliser "import" pour les nouveaux projets et non pas "requis".

4
Nir O.