Una guía completa del «sistema de módulos» en TypeScript (con ejemplos)
On octubre 16, 2021 by adminEstándar de módulos
EcmaScript ha estandarizado cómo debemos importar módulos en JavaScript y este estándar gira en torno a dos palabras clave, import
y export
. Como el sistema de módulos en JavaScript se está popularizando, hay algunos cambios nuevos que llegan a este estándar cada año.
En este tutorial, vamos a ver la semántica del sistema de módulos en TypeScript (que en su mayor parte se parece al estándar de EcmaScript) y cómo funcionan las palabras clave import
y export
.
Exportaciones con nombre
La palabra clave export
hace que un valor (variable) definido en un archivo esté disponible para su importación en otros archivos de módulos. Cuando un módulo es importado en otro archivo de módulo usando la palabra clave import
, el archivo importador puede especificar los valores a los que quiere acceder desde el módulo importado.
// program.ts
import { A } from 'path/to/values';console.log( A ); // "Apple"
En el ejemplo anterior, estamos importando values.ts
en el archivo program.ts
y extrayendo el miembro exportado A
. Esto significa que values.ts
debe exportar el valor A
de alguna forma. Hay múltiples formas de exponer A
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A, B, C, D };
Puedes exportar cualquier valor accesible de TypeScript con un identificador válido (nombre). Aquí en este ejemplo, estamos exportando una variable A
(también podría ser una constante), la clase B
, y así sucesivamente. Usando la sintaxis export {}
, puedes exportar tantos valores como quieras y cualquiera puede importarlos usando la sintaxis import { A, B }
. No es necesario importar todos los valores exportados.
Hay otra forma de exportar un valor donde fue declarado.
// values.ts
export const A = { name: "Apple" };
export class B {}
export function C(){}
export enum D{}
En el ejemplo anterior, hemos exportado todos los valores donde fueron declarados. Tampoco es necesario inicializar el valor, puede hacerlo más adelante en el archivo. Esta es una forma mucho más agradable en comparación con la anterior.
En ambos casos, todos sus miembros de exportación son accesibles dentro del mismo archivo si usted necesita usarlos. Tener la palabra clave export
en un valor no cambia su comportamiento dentro del archivo del módulo.
Exportación por defecto
Hasta ahora hemos exportado valores que sólo pueden ser importados proporcionando el nombre de los miembros de exportación como import { A } from 'path/to/values'
donde A
aquí es un miembro de exportación de values.ts
. Estos se llaman exportaciones con nombre.
También se permite exportar un único valor que puede ser importado sin especificar un nombre. Este valor debe ser identificado con la palabra clave default
junto con la palabra clave export
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class A {}
function A(){}
enum A{}export default A;
Sólo se permite exportar un único valor como exportación por defecto, por lo que no tiene la expresión export default {...}
para trabajar. A diferencia de las exportaciones con nombre, un valor no puede ser exportado como predeterminado con una declaración de variable (o constante) a excepción de una declaración function
y 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
Sin embargo, puede exportar un valor directamente como exportación predeterminada. Esto significa que cualquier expresión de JavaScript puede ser exportada como valor de exportación por defecto.
// values.ts
export default "Hello"; // string
export default {}; // object
export default () => undefined; // function
export default function(){}; // function
export default class{}; // class
Dado que la exportación por defecto carece de un nombre de exportación, podemos proporcionar cualquier nombre para ella durante la importación. Puede tener exportaciones con nombre y una exportación por defecto en el mismo archivo de módulo. Del mismo modo, puede importar la exportación por defecto y los valores de exportación con nombre en una sola declaración import
como se muestra a continuación.
import Apple, { B, C } from 'path/to/values';
En el ejemplo anterior, el Apple
es el nombre que utilizamos para recoger la exportación por defecto de la values.ts
. Puede eliminar la expresión { B, C }
o el nombre Apple
de la declaración import
si no va a utilizarlo.
💡 Al igual que las exportaciones con nombre, no es obligatorio importar el valor de la exportación por defecto cuando se utiliza la declaración
import
. Pero, elApple
debe ir antes de las exportaciones con nombre en la firma de la declaraciónimport
.
También puede utilizar la sintaxis as default
en la firma export {}
para exportar un valor por defecto junto con otras exportaciones con nombre. Esto se llama aliasing y se explica en la siguiente sección.
// 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 razón por la que no uso y recomiendo la exportación
default
tiene que ver con la experiencia del desarrollador. Como hemos aprendido, usted puede proporcionar cualquier nombre para el memeber de exportación por defecto de un módulo en la declaraciónimport
. Si este valor se importa en varios archivos, puede hacer referencia a él con cualquier nombre de su elección y tener diferentes nombres para el mismo miembro de exportación del mismo módulo es un mal DX.
Importación y exportación Alias
En el caso de la exportación por defecto, puede hacer referencia al valor con cualquier nombre de su elección en la declaración import
. Sin embargo, ese no es el caso de las exportaciones con nombre. Sin embargo, puede utilizar la palabra clave as
para hacer referencia a un valor de exportación con nombre con el nombre de su elección.
// program.ts
import A, { B as Ball } from 'path/to/values';console.log( B ); // ❌ Error: Cannot find name 'B'.
console.log( Ball ); // ✅ legal
En el ejemplo anterior, estamos importando B
desde el values.ts
pero ha sido renombrado a Ball
. Por lo tanto, en el archivo program.ts
, se utilizaría Apple
en lugar de B
. Cuando se pone un alias a un miembro de exportación, el nombre original ya no existe en el ámbito global, por lo que B
ya no existirá en el program.ts
.
💡 No se puede poner un alias al miembro de exportación por defecto, ya que se puede proporcionar cualquier nombre de su elección, pero se puede utilizar
import { default as defaultValue, }
.
También se permite poner un alias a un valor mientras se exporta utilizando la palabra clave 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 };
En el ejemplo anterior, hemos exportado la variable A
como exportación por defecto y la función C
como Cat
. Por lo tanto, cualquiera que importe estos valores desde el archivo values.ts
debe utilizar la sintaxis de importación por defecto para A
y Cat
para el valor C
.
Importar todas las exportaciones con nombre
Puede importar todas las exportaciones con nombre desde un archivo utilizando la sintaxis * as
.
// program.ts
import Apple, * as values from 'path/to/values';console.log( values.B );
console.log( values.C );
console.log( values.D );
Aquí, todas las exportaciones con nombre de values.ts
se guardarán bajo values
que será un objeto donde el keys
será el nombre de los miembros de la exportación y values
será los valores exportados de los miembros de la exportación.
Reexportar
Un valor importado puede ser re-exportado de la manera normal. Cuando se importa algo, se tiene una referencia al valor de importación y se puede utilizar el mismo valor en la sintaxis export
. No hay nada malo en ello.
// lib.ts
import A, { B, C as Cat, D } from 'path/to/values';export { D as default, A, B, Cat as C, D };
En el ejemplo anterior, ha importado algunos valores de values.ts
dentro de lib.ts
y exportado según su preferencia. Esto es genial si desea utilizar estas importaciones en lib.ts
así como exportar algunos de ellos si otro archivo los necesita cuando importa desde el lib.ts
.
Sin embargo, también tenemos la sentencia export ... from
para exportar valores de un archivo sin tener que importarlos primero. Esto es genial cuando quieres mantener un único punto de entrada para todas las importaciones de tu módulo.
// 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 };
En el ejemplo anterior, estamos exportando algunos de los miembros de exportación de values-1.ts
y values-2.ts
desde el fichero lib.ts
(excepto la exportación por defecto). También podemos utilizar el alias en la sintaxis export ... from
.
La sintaxis de reexportación no afecta al archivo actual, por lo tanto, puede tener sus exportaciones regulares en el archivo, así como se muestra arriba. A diferencia de la palabra clave export
, la sintaxis export ... from
no permite el acceso a los miembros reexportados. Por lo tanto, usted no será capaz de acceder a P
o Orange
en el archivo 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'.
Usted puede exportar todas las exportaciones con nombre de un archivo utilizando export * from
sintaxis como nosotros como usted tiene la capacidad de cambiar el nombre mientras que la exportación utilizando export * as ... from
.
// lib.ts
export * from 'path/to/values-1';
export * as values2 from 'path/to/other/values-2';
Desde el ejemplo anterior, si values-1.ts
exporta P
y Q
, entonces cualquiera puede importar P
y Q
desde lib.ts
. Sin embargo, todas las exportaciones con nombre de values-2.ts
se reexportan como values2
exportación con nombre de lib.ts
, por lo que es necesario importarlos utilizando la siguiente sintaxis.
// program.ts
import { P, Q, values2 } from 'path/to/lib';
No puede acceder a la exportación por defecto utilizando la sintaxis export ... from
de forma normal. Pero puede exportar la exportación por defecto utilizando la palabra clave default
.
// lib.ts
export { default } from 'path/to/values-1';
export { default as Apple } from 'path/to/values-2';
En el ejemplo anterior, la exportación por defecto de lib.ts
es el valor de exportación por defecto de values-2.ts
. El valor de exportación por defecto de values-2
se exportará de lib.ts
como el Apple
llamado export.
Importar por efecto secundario
¿Se imagina importar un archivo sin especificar los miembros exportados?
import 'path/to/action';
En el ejemplo anterior, estamos importando action.ts
pero hemos especificado cualquier miembro exportado por el action.ts
. Esta es una sintaxis válida, pero se utiliza para producir un resultado muy diferente.
Cuando se importa un archivo utilizando la palabra clave import
, primero se ejecuta y luego todos los miembros de exportación están disponibles para la importación. Por lo tanto, en el siguiente ejemplo, un archivo de importación PI
obtendría el 3.14
como un número floating-point
.
export const PI = parseFloat( "3.14" ); // 3.14
Siguiendo la misma lógica, si desea crear un efecto secundario mediante la importación de un archivo, entonces usted puede hacer totalmente eso. Así, por ejemplo, si hay un archivo que inicializa algunas variables globales, puede importar eso sin especificar los miembros exportados de ese archivo (también podría exportar ninguno en primer lugar).
El compilador de TypeScript puede crear un archivo de paquete de JavaScript (.js
) compilando dos o más archivos .ts
juntos. Esto se hace normalmente proporcionando un archivo TypeScript de entrada y luego recorriendo su árbol de dependencias.
Un árbol de dependencias se construye mirando todas las declaraciones import
(dependencias) del archivo de entrada y siguiendo las dependencias de estos archivos importados. Si desea incluir un archivo cuyo código podría crear algunos efectos secundarios en tiempo de ejecución, entonces usted debe importar ese archivo sin especificar los miembros exportados en la sintaxis import
. Esto se puede hacer en el archivo de entrada o dentro de cualquier archivo que está dentro del árbol de dependencia.
Deja una respuesta