¿No entendí cómo los categorizamos?
Los espacios de nombres se utilizan para organizar / encapsular su código. Los módulos externos se utilizan para organizar / encapsular su código Y para localizar su código en tiempo de ejecución. En la práctica, tiene dos opciones en tiempo de ejecución: 1) combinar todo el código transpilado en un archivo, o 2) usar módulos externos y tener varios archivos y requerir algún otro mecanismo para acceder a esos archivos.
¿Cuándo exportar una clase, un espacio de nombres o un paquete?
Para que un tipo o valor sea visible fuera del archivo en el que se encuentra, debe exportarlo si está dentro de un espacio de nombres. Ya sea que lo exporte al nivel superior o dentro de un espacio de nombres, decidirá si ahora está en un módulo externo.
Si exportamos el paquete / espacio de nombres, todas las clases dentro de ese se exportan o deben exportarse explícitamente
Las clases en un espacio de nombres siempre deberán exportarse explícitamente para que la clase sea visible en tiempo de compilación fuera del archivo en el que está definida.
¿Cómo se puede importar / requerir cada uno de ellos?
Esto depende de si está utilizando módulos externos. Siempre será necesario importar un módulo externo para "usarlo". Importar un espacio de nombres que no está en un módulo externo en realidad es solo proporcionar un alias para el espacio de nombres; aún debe prefijar el tipo / lo que sea con el alias (y es por eso que generalmente no desea usar espacios de nombres con módulos externos; hacerlo significa que siempre tiene que usar un prefijo al hacer referencia a algo proporcionado por el módulo externo). Los espacios de nombres que no están en un módulo externo pueden abarcar archivos, por lo que si está en el mismo espacio de nombres, puede hacer referencia a cualquier cosa exportada por el espacio de nombres sin necesidad de ningún tipo de importación.
Para comprender realmente lo anterior, necesita algunos conocimientos previos. La clave para entender con referencias / espacios de nombres / módulos externos es lo que hacen estas construcciones en tiempo de compilación y lo que hacen en tiempo de ejecución.
Las directivas de referencia se utilizan en tiempo de compilación para localizar información de tipo. Su fuente tiene un símbolo particular. ¿Cómo localiza el compilador de TypeScript la definición de ese símbolo? La directiva de referencia ha sido subsumida en gran medida por el mecanismo tsconfig.json: usando tsconfig.json, le indica al compilador dónde están todas sus fuentes.
Los espacios de nombres pueden contener definiciones de tipo y / o implementación. Si un espacio de nombres contiene solo información de tipo, entonces no tiene ninguna manifestación de tiempo de ejecución; puede verificar esto mirando la salida JS y encontrando un archivo JS vacío. Si un espacio de nombres tiene código de implementación, entonces el código se envuelve dentro de un cierre que se asigna a una variable global con el mismo nombre que el espacio de nombres. Con los espacios de nombres anidados, habrá una variable global para el espacio de nombres raíz. Nuevamente, verifique la salida JS. Los espacios de nombres son históricamente la forma en que las bibliotecas del lado del cliente de JS han intentado evitar el problema de las colisiones de nombres. La idea es envolver toda su biblioteca en un solo cierre y luego exponer una huella global lo más pequeña posible, solo una variable global que hace referencia al cierre. Bueno, el problema sigue siendo que ha reclamado un nombre en el espacio global. ¿Y si quisiera, digamos, dos versiones de una biblioteca? Un espacio de nombres de TypeScript todavía tiene el problema de cómo ubicar la fuente para el espacio de nombres. Es decir, el código fuente que hace referencia a AB todavía tiene el problema de decirle al compilador cómo ubicar AB, ya sea usando directivas de referencia o usando tsconfig.json. O colocando el espacio de nombres en un módulo externo y luego importando el módulo externo.
Los módulos externos se originaron con JS del lado del servidor. Existe una correspondencia uno a uno entre un módulo externo y un archivo en el sistema de archivos. Puede utilizar la estructura de directorios del sistema de archivos para organizar módulos externos en una estructura anidada. La importación de un módulo externo generalmente siempre introducirá una dependencia de tiempo de ejecución en ese módulo externo (la excepción es cuando importa un módulo externo pero luego no usa ninguna de sus exportaciones en la posición de valor, es decir, solo importa el módulo externo para obtener la información de su tipo). Un módulo externo está implícito en un cierre, y esta es la clave: el usuario del módulo puede asignar el cierre a cualquier variable local que desee. TypeScript / ES6 agrega sintaxis adicional sobre la asignación de las exportaciones de los módulos externos a los nombres locales, pero esto es solo una sutileza. En el lado del servidor, localizar un módulo externo es relativamente sencillo: simplemente ubique el archivo que representa el módulo externo en el sistema de archivos local. Si desea utilizar módulos externos en el lado del cliente, en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador. en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador. en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador.
Escenario del mundo real: AngularJS. Suponga que los módulos externos no existen, use un solo espacio de nombres para limitar la contaminación del espacio global (en el ejemplo a continuación, una sola variable MyApp es todo lo que hay en el espacio global), exporte solo interfaces y use la inyección de dependencia AngularJS para realizar implementaciones disponible para su uso. Coloque todas las clases en una raíz de directorio, agregue un tsconfig.json a la raíz, instale tipificaciones de angularjs en la misma raíz de directorio para que tsconfig.json también lo recoja, combine toda la salida en un archivo JS. Esto funcionará bien para la mayoría de los proyectos si la reutilización del código no es una gran preocupación.
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);
}
Usar IIFE para evitar contaminar el alcance del tiempo de ejecución global. En este ejemplo, no se crea ninguna variable global. (Se asume un tsconfig.json).
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;
}
})();
Con IIFE puede eliminar la introducción de MyApp como una variable global en el primer ejemplo.
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);
})();