TypeScript

TypeScript

Tutorial TypeScript: Introducción a módulos

Aprende el sistema de módulos ES6 en TypeScript, su sintaxis, importaciones, exportaciones y resolución para código modular y escalable.

Aprende TypeScript y certifícate

Sistema de módulos ES6

El sistema de módulos ES6 (ECMAScript 2015) representa un avance significativo en la organización del código JavaScript, que TypeScript adopta y extiende con sus capacidades de tipado estático. Este sistema permite dividir el código en unidades independientes y reutilizables, facilitando el mantenimiento y la escalabilidad de las aplicaciones.

Antes de ES6, JavaScript carecía de un sistema de módulos nativo, lo que obligaba a los desarrolladores a utilizar patrones como el patrón módulo o bibliotecas externas como RequireJS o CommonJS. Con la llegada de ES6, se estandarizó finalmente una sintaxis oficial para trabajar con módulos.

Características principales del sistema de módulos ES6

  • Ámbito léxico: Cada módulo tiene su propio ámbito, lo que significa que las variables, funciones y clases declaradas en un módulo no están disponibles globalmente a menos que se exporten explícitamente.

  • Carga única: Los módulos se ejecutan una sola vez cuando se importan por primera vez, y las importaciones posteriores reutilizan la misma instancia del módulo.

  • Estructura estática: Las declaraciones de importación y exportación deben estar en el nivel superior del módulo, no pueden estar dentro de bloques condicionales o funciones.

  • Soporte para tipos: En TypeScript, los módulos pueden exportar no solo valores sino también definiciones de tipos, lo que mejora la documentación y la verificación del código.

Sintaxis básica en TypeScript

Un módulo ES6 en TypeScript es simplemente un archivo que contiene código TypeScript con al menos una declaración de exportación o importación. Veamos un ejemplo básico:

// math.ts
export function sum(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

// Constante que no se exporta (privada al módulo)
const PI = 3.14159;

// Función que utiliza la constante privada
export function calculateCircleArea(radius: number): number {
  return PI * radius * radius;
}

En este ejemplo, las funciones sum, multiply y calculateCircleArea están disponibles para ser importadas por otros módulos, mientras que la constante PI permanece privada dentro del módulo.

Exportaciones por defecto

Además de las exportaciones con nombre, ES6 permite una exportación por defecto por módulo:

// user.ts
export default class User {
  constructor(public name: string, public email: string) {}
  
  greet(): string {
    return `Hello, my name is ${this.name}`;
  }
}

// También podemos tener exportaciones con nombre junto a la exportación por defecto
export function validateEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

La exportación por defecto se utiliza cuando un módulo exporta principalmente un solo valor, función o clase.

Reexportación de módulos

El sistema de módulos ES6 permite reexportar elementos de otros módulos, lo que facilita la creación de módulos que agrupan funcionalidades relacionadas:

// index.ts - Módulo que agrupa y reexporta
export { sum, multiply } from './math';
export { default as User, validateEmail } from './user';

// También podemos renombrar al reexportar
export { sum as addition } from './math';

Esta técnica es útil para crear APIs públicas bien organizadas, ocultando la estructura interna de los módulos.

Importaciones dinámicas

ES6 también introdujo la capacidad de importar módulos dinámicamente usando la función import(), que devuelve una promesa:

// En TypeScript podemos tipar el resultado de la importación dinámica
async function loadMathModule() {
  try {
    const math = await import('./math');
    console.log(math.sum(5, 3)); // 8
  } catch (error) {
    console.error('Error loading module:', error);
  }
}

// O usar la sintaxis then/catch
function loadUserModule() {
  import('./user')
    .then(module => {
      const admin = new module.default('Admin', 'admin@example.com');
      console.log(admin.greet());
    })
    .catch(error => {
      console.error('Error loading module:', error);
    });
}

Las importaciones dinámicas son especialmente útiles para:

  • Carga bajo demanda de módulos
  • Implementación de code splitting
  • Carga condicional basada en la lógica de la aplicación

Interoperabilidad con otros sistemas de módulos

TypeScript permite trabajar con diferentes sistemas de módulos mediante la configuración del compilador. En el archivo tsconfig.json, podemos especificar el sistema de módulos de destino:

{
  "compilerOptions": {
    "module": "es2015", // También: "commonjs", "amd", "system", "umd", "esnext"
    // Otras opciones...
  }
}

Esta flexibilidad permite que el código TypeScript se integre con diferentes entornos y sistemas de módulos.

Ventajas del sistema de módulos ES6 en TypeScript

  • Encapsulación: Cada módulo tiene su propio ámbito, lo que evita la contaminación del espacio de nombres global.

  • Organización del código: Facilita la división del código en unidades lógicas y manejables.

  • Tipado explícito: TypeScript añade la capacidad de exportar e importar tipos, interfaces y otras construcciones de tipado.

  • Compatibilidad con herramientas modernas: El sistema de módulos ES6 es compatible con empaquetadores como Webpack, Rollup y Parcel.

  • Carga asíncrona: Soporte nativo para importaciones dinámicas que permiten cargar código bajo demanda.

El sistema de módulos ES6 en TypeScript proporciona una base sólida para construir aplicaciones modulares y bien estructuradas, aprovechando tanto las ventajas de la modularidad de ES6 como las capacidades de tipado estático de TypeScript.

Import y export

Las declaraciones import y export son la base del sistema de módulos ES6 en TypeScript, permitiendo compartir código entre diferentes archivos de manera estructurada y segura. Estas declaraciones definen qué elementos están disponibles fuera del módulo y cómo pueden ser utilizados por otros módulos.

Exportación de elementos

TypeScript ofrece varias formas de exportar elementos desde un módulo:

Exportaciones con nombre

La forma más común de exportar elementos es mediante exportaciones con nombre, que permiten exponer múltiples valores desde un mismo módulo:

// logger.ts
export const LOG_LEVEL = {
  INFO: 'info',
  WARNING: 'warning',
  ERROR: 'error'
};

export interface LogMessage {
  level: string;
  message: string;
  timestamp: Date;
}

export class Logger {
  log(level: string, message: string): void {
    console.log(`[${level.toUpperCase()}]: ${message}`);
  }
}

export function formatLogMessage(msg: LogMessage): string {
  return `[${msg.level}][${msg.timestamp.toISOString()}] ${msg.message}`;
}

En este ejemplo, exportamos una constante, una interfaz, una clase y una función, todos con nombres específicos que se utilizarán al importarlos.

Exportación por defecto

Cuando un módulo tiene un elemento principal, podemos utilizar la exportación por defecto:

// config.ts
interface Config {
  apiUrl: string;
  timeout: number;
  retries: number;
}

// Exportación por defecto (solo puede haber una por módulo)
export default {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
} as Config;

También podemos exportar por defecto clases, funciones o cualquier otro valor:

// api-client.ts
export default class ApiClient {
  constructor(private baseUrl: string) {}
  
  async get<T>(endpoint: string): Promise<T> {
    const response = await fetch(`${this.baseUrl}/${endpoint}`);
    return response.json();
  }
}

Exportaciones combinadas

Un módulo puede combinar exportaciones con nombre y una exportación por defecto:

// validation.ts
// Exportación con nombre
export function isEmail(value: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(value);
}

// Exportación por defecto
export default class Validator {
  validate(schema: Record<string, (value: any) => boolean>, data: any): boolean {
    for (const [field, validator] of Object.entries(schema)) {
      if (!validator(data[field])) {
        return false;
      }
    }
    return true;
  }
}

Exportación de tipos

Una característica poderosa de TypeScript es la capacidad de exportar tipos, interfaces, enums y otras construcciones de tipado:

// types.ts
export type UserId = string;

export interface User {
  id: UserId;
  name: string;
  email: string;
  role: UserRole;
}

export enum UserRole {
  ADMIN = 'admin',
  EDITOR = 'editor',
  VIEWER = 'viewer'
}

export interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

Importación de elementos

TypeScript ofrece varias formas de importar elementos desde otros módulos:

Importación de elementos con nombre

Para importar elementos exportados con nombre:

// app.ts
import { LOG_LEVEL, Logger, LogMessage, formatLogMessage } from './logger';

const logger = new Logger();
logger.log(LOG_LEVEL.INFO, 'Aplicación iniciada');

const message: LogMessage = {
  level: LOG_LEVEL.WARNING,
  message: 'Recurso no encontrado',
  timestamp: new Date()
};

console.log(formatLogMessage(message));

Importación de exportaciones por defecto

Para importar un elemento exportado por defecto:

// app.ts
import config from './config';
import ApiClient from './api-client';

const client = new ApiClient(config.apiUrl);
client.get('users').then(data => console.log(data));

Importaciones combinadas

Podemos combinar importaciones por defecto y con nombre en una sola declaración:

// app.ts
import Validator, { isEmail } from './validation';

const validator = new Validator();
console.log(isEmail('user@example.com')); // true

const userSchema = {
  email: isEmail,
  name: (value: string) => value.length > 0
};

validator.validate(userSchema, {
  email: 'user@example.com',
  name: 'John'
}); // true

Importación de todo el módulo

Podemos importar todo el contenido de un módulo como un objeto:

// app.ts
import * as loggerModule from './logger';

const logger = new loggerModule.Logger();
logger.log(loggerModule.LOG_LEVEL.INFO, 'Aplicación iniciada');

Renombrar importaciones

Podemos renombrar elementos al importarlos para evitar conflictos de nombres:

// app.ts
import { Logger as LogService, LOG_LEVEL as LogLevels } from './logger';
import { User as UserModel } from './types';

const logger = new LogService();
logger.log(LogLevels.INFO, 'Procesando usuario');

const user: UserModel = {
  id: '123',
  name: 'Alice',
  email: 'alice@example.com',
  role: 'admin'
};

Importación de tipos

TypeScript permite importar tipos, interfaces y otras construcciones de tipado:

// user-service.ts
import { User, UserId, UserRole, ApiResponse } from './types';

export async function getUser(id: UserId): Promise<ApiResponse<User>> {
  // Implementación...
  return {
    data: {
      id,
      name: 'John Doe',
      email: 'john@example.com',
      role: UserRole.VIEWER
    },
    status: 200,
    message: 'Success'
  };
}

Importación de archivos no TypeScript

TypeScript también permite importar recursos que no son código TypeScript, como archivos JSON, CSS o imágenes, si se configura adecuadamente:

// Para importar JSON (requiere "resolveJsonModule": true en tsconfig.json)
import settings from './settings.json';
console.log(settings.theme);

// Para importar otros recursos (requiere definiciones de tipos adecuadas)
import './styles.css';
import logoUrl from './logo.png';

Importaciones de solo tipo

TypeScript 3.8 introdujo la sintaxis de importación de solo tipo, útil para evitar que las importaciones de tipos afecten al código JavaScript generado:

// Solo importa el tipo, no el valor
import type { User, ApiResponse } from './types';

function processUser(user: User): ApiResponse<User> {
  // Implementación...
  return {
    data: user,
    status: 200,
    message: 'Processed'
  };
}

Reexportación de módulos

Podemos reexportar elementos de otros módulos para crear APIs más limpias:

// index.ts
// Reexportar elementos individuales
export { Logger, LOG_LEVEL } from './logger';
export { default as config } from './config';

// Reexportar y renombrar
export { isEmail as validateEmail } from './validation';

// Reexportar todo
export * from './types';

// Reexportar todo excepto algunos elementos
export * from './utils';
export { default as Utils } from './utils';

Las declaraciones import y export en TypeScript proporcionan una forma flexible y potente de organizar el código, facilitando la creación de aplicaciones modulares y mantenibles con un sistema de tipos robusto.

Módulos vs scripts

En TypeScript, la distinción entre módulos y scripts representa dos enfoques fundamentalmente diferentes para organizar y ejecutar código. Entender esta diferencia es crucial para estructurar correctamente las aplicaciones y evitar problemas comunes de ámbito y accesibilidad.

Ámbito global vs ámbito de módulo

La diferencia más significativa entre scripts y módulos radica en cómo manejan el ámbito de las declaraciones:

  • Scripts: Operan en el ámbito global. Todas las variables, funciones y clases declaradas en un script están disponibles globalmente y pueden ser accedidas por cualquier otro script que se cargue en la misma página o contexto.

  • Módulos: Utilizan un ámbito local. Las declaraciones dentro de un módulo están encapsuladas y solo son accesibles dentro del propio módulo, a menos que se exporten explícitamente.

Veamos un ejemplo que ilustra esta diferencia:

// Como script (archivo.ts sin import/export)
function saludar(nombre: string): void {
  console.log(`Hola, ${nombre}!`);
}

const usuario = "Ana";
saludar(usuario); // Funciona correctamente
// Como módulo (modulo.ts con al menos un import/export)
export function saludar(nombre: string): void {
  console.log(`Hola, ${nombre}!`);
}

const usuario = "Ana"; // Esta constante es privada al módulo
saludar(usuario); // Funciona correctamente

En el primer caso, saludar y usuario están en el ámbito global. En el segundo, solo saludar está disponible fuera del módulo, mientras que usuario permanece privado.

Identificación de módulos vs scripts

TypeScript utiliza una regla simple para determinar si un archivo es un módulo o un script:

  • Si un archivo contiene al menos una declaración import o export en el nivel superior, se trata como un módulo.
  • Si un archivo no contiene ninguna declaración import o export, se trata como un script.

Esta distinción es automática y tiene importantes implicaciones para el comportamiento del código.

Declaraciones en el ámbito global

En un script, las declaraciones pueden colisionar con otras declaraciones globales:

// script1.ts
const MAX_USERS = 100;
function initialize() { /* ... */ }

// script2.ts
const MAX_USERS = 200; // Error: Identificador duplicado 'MAX_USERS'
function initialize() { /* ... */ } // Error: Identificador duplicado 'initialize'

En cambio, en los módulos no hay riesgo de colisión:

// modulo1.ts
export const MAX_USERS = 100;
export function initialize() { /* ... */ }

// modulo2.ts
export const MAX_USERS = 200; // No hay error, son módulos diferentes
export function initialize() { /* ... */ } // No hay error, son módulos diferentes

Acceso a variables globales

Los scripts pueden acceder y modificar variables globales directamente:

// script.ts
// Accede a una variable global del navegador
window.onload = () => {
  console.log("Página cargada");
};

// Modifica el objeto global
globalThis.miVariable = "valor global";

Los módulos también pueden acceder a variables globales, pero es más explícito:

// modulo.ts
export function inicializarUI() {
  // Accede a una variable global del navegador
  window.onload = () => {
    console.log("Página cargada desde módulo");
  };
  
  // Modifica el objeto global (menos común en módulos)
  globalThis.configuracion = { tema: "oscuro" };
}

Declaraciones de tipos

El manejo de las declaraciones de tipos también difiere entre scripts y módulos:

// Como script
interface Usuario {
  nombre: string;
  edad: number;
}

// Esta interfaz está disponible globalmente
const usuario: Usuario = { nombre: "Carlos", edad: 30 };
// Como módulo
export interface Usuario {
  nombre: string;
  edad: number;
}

// La interfaz solo está disponible si se importa
const usuario: Usuario = { nombre: "Carlos", edad: 30 };

Archivos de declaración (.d.ts)

Los archivos de declaración se comportan de manera especial:

  • Un archivo .d.ts sin declaraciones import/export añade tipos al ámbito global.
  • Un archivo .d.ts con declaraciones import/export define un módulo de declaración.
// global.d.ts (ámbito global)
interface Window {
  analytics: {
    trackEvent(name: string, properties?: object): void;
  }
}

// api.d.ts (módulo de declaración)
export interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

Conversión entre scripts y módulos

Convertir un script en un módulo es tan simple como añadir una exportación:

// Antes: script.ts
function utilidad() {
  return "Función útil";
}

// Después: módulo.ts
export function utilidad() {
  return "Función útil";
}

Para mantener la compatibilidad con código existente al convertir scripts en módulos, se puede usar la exportación de ámbito global:

// modulo.ts
export {};  // Convierte el archivo en un módulo

// Declara en el ámbito global
declare global {
  interface Window {
    misFunciones: {
      calcular(): number;
    }
  }
}

// Implementa la funcionalidad
window.misFunciones = {
  calcular: () => 42
};

Consideraciones prácticas

Al decidir entre módulos y scripts, considera:

  • Mantenibilidad: Los módulos facilitan la organización del código en unidades independientes y reutilizables.

  • Encapsulación: Los módulos permiten ocultar detalles de implementación y exponer solo una API pública.

  • Compatibilidad: Los scripts pueden ser necesarios para ciertos entornos o casos de uso específicos, como scripts de usuario o extensiones de navegador.

  • Herramientas de construcción: Los módulos funcionan mejor con empaquetadores modernos como Webpack, Rollup o esbuild.

// Ejemplo de módulo bien estructurado
// api-client.ts
export interface RequestOptions {
  headers?: Record<string, string>;
  timeout?: number;
}

export class ApiClient {
  private baseUrl: string;
  
  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }
  
  async get<T>(endpoint: string, options?: RequestOptions): Promise<T> {
    const url = `${this.baseUrl}/${endpoint}`;
    const response = await fetch(url, {
      headers: options?.headers,
      signal: options?.timeout ? AbortSignal.timeout(options.timeout) : undefined
    });
    
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    
    return response.json();
  }
}

Migración de scripts a módulos

Al migrar una base de código de scripts a módulos, sigue estos pasos:

  • Identifica dependencias: Determina qué scripts dependen de otros.

  • Comienza por los más básicos: Convierte primero los scripts con menos dependencias.

  • Exporta interfaces públicas: Decide qué funcionalidades deben exportarse.

  • Actualiza referencias: Modifica el código que utiliza los scripts convertidos para que importe los módulos.

  • Prueba incrementalmente: Verifica que cada conversión funcione antes de continuar.

// Antes (script)
function formatDate(date) {
  return new Intl.DateTimeFormat('es').format(date);
}

// Después (módulo)
export function formatDate(date: Date): string {
  return new Intl.DateTimeFormat('es').format(date);
}

En resumen, mientras que los scripts son adecuados para pequeños fragmentos de código o scripts de utilidad, los módulos ofrecen una estructura más robusta para aplicaciones complejas, facilitando la organización, el mantenimiento y la escalabilidad del código TypeScript.

Resolución de módulos

La resolución de módulos es el proceso mediante el cual TypeScript localiza y carga los archivos correspondientes a las declaraciones de importación en el código. Este mecanismo es fundamental para el correcto funcionamiento del sistema de módulos, ya que determina cómo el compilador encuentra los módulos referenciados en las sentencias import.

TypeScript implementa diferentes estrategias de resolución de módulos para adaptarse a diversos entornos y configuraciones de proyecto. Entender estas estrategias es esencial para evitar errores comunes y estructurar correctamente las aplicaciones.

Estrategias de resolución de módulos

TypeScript soporta principalmente dos estrategias de resolución:

  • Clásica: Basada en la resolución de Node.js, utilizada para módulos con rutas relativas.
  • Node: Emula el mecanismo de resolución de Node.js para módulos tanto relativos como no relativos.

La estrategia utilizada se configura mediante la opción moduleResolution en el archivo tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node", // Opciones: "node", "classic", "bundler", "node16" o "nodenext"
    // Otras opciones...
  }
}

Resolución de rutas relativas

Cuando importamos un módulo utilizando una ruta relativa (que comienza con ./ o ../), TypeScript busca el archivo siguiendo estas reglas:

// Importación con ruta relativa
import { Usuario } from './modelos/usuario';

El proceso de resolución para rutas relativas sigue este orden:

  1. Busca el archivo exacto: ./modelos/usuario.ts
  2. Busca el archivo con extensiones: ./modelos/usuario.js, ./modelos/usuario.jsx, ./modelos/usuario.d.ts
  3. Busca un archivo index en el directorio: ./modelos/usuario/index.ts, ./modelos/usuario/index.js, etc.

Por ejemplo, si tenemos esta estructura de archivos:

src/
├── app.ts
└── modelos/
    ├── usuario.ts
    └── producto/
        └── index.ts

Estas importaciones se resolverán así:

// En app.ts
import { Usuario } from './modelos/usuario'; // Resuelve a ./modelos/usuario.ts
import { Producto } from './modelos/producto'; // Resuelve a ./modelos/producto/index.ts

Resolución de módulos no relativos

Para importaciones no relativas (sin ./ o ../), TypeScript utiliza un algoritmo más complejo que depende de la estrategia de resolución configurada:

// Importación no relativa
import { useState } from 'react';

Con la estrategia node, el proceso sigue estas reglas:

  1. Busca en la carpeta node_modules del directorio actual
  2. Si no lo encuentra, sube un nivel y busca en node_modules allí
  3. Continúa subiendo hasta encontrar el módulo o llegar a la raíz del sistema de archivos

Dentro de cada carpeta node_modules, busca:

  1. Un archivo con el nombre exacto o con extensiones compatibles
  2. El campo "main" en el package.json del paquete
  3. Un archivo index.js o index.ts en la carpeta del paquete

Mapeo de rutas con path aliases

TypeScript permite definir alias de rutas para simplificar las importaciones y evitar rutas relativas complejas:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@modelos/*": ["src/modelos/*"],
      "@utils/*": ["src/utils/*"],
      "@components": ["src/components/index"]
    }
  }
}

Con esta configuración, podemos importar así:

// Sin alias
import { Usuario } from '../../modelos/usuario';

// Con alias
import { Usuario } from '@modelos/usuario';
import { formatearFecha } from '@utils/fecha';
import { Button, Card } from '@components';

Los alias de rutas son especialmente útiles en proyectos grandes con estructuras de directorios profundas, ya que:

  • Simplifican las importaciones
  • Evitan rutas relativas complejas (../../../../)
  • Facilitan la refactorización de la estructura de archivos

Resolución de tipos de declaración

TypeScript también busca archivos de declaración (.d.ts) para proporcionar información de tipos para módulos JavaScript:

  1. Busca archivos .d.ts junto a los archivos JavaScript
  2. Busca en el directorio @types dentro de node_modules para definiciones de tipos de terceros

Por ejemplo, para usar una biblioteca como lodash:

import _ from 'lodash'; // TypeScript busca tipos en @types/lodash

Para que esto funcione, necesitamos instalar los tipos:

npm install --save-dev @types/lodash

Extensiones de archivo en importaciones

En TypeScript, normalmente omitimos las extensiones de archivo en las importaciones:

// Recomendado
import { Usuario } from './modelos/usuario';

// No es necesario (y puede causar problemas)
import { Usuario } from './modelos/usuario.ts';

Sin embargo, con la introducción de ESM en Node.js, las extensiones se vuelven obligatorias. Para proyectos que apuntan a este entorno, TypeScript ofrece las estrategias node16 o nodenext:

{
  "compilerOptions": {
    "module": "node16",
    "moduleResolution": "node16"
  }
}

Con esta configuración, las importaciones deben incluir la extensión que tendrá el archivo después de la compilación:

// Para módulos ESM en Node.js
import { Usuario } from './modelos/usuario.js'; // Nota la extensión .js, no .ts

Resolución de módulos en monorepos

En proyectos monorepo (múltiples paquetes en un solo repositorio), la resolución de módulos puede complicarse. TypeScript ofrece opciones para facilitar las referencias entre paquetes:

{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "references": [
    { "path": "../paquete-comun" }
  ]
}

Esta configuración permite:

  • Referencias de proyecto: Importar directamente desde otros paquetes del monorepo
  • Compilación incremental: Solo recompilar lo que ha cambiado

Personalización de la resolución con plugins

Para casos más complejos, TypeScript permite plugins de resolución que pueden modificar el proceso:

{
  "compilerOptions": {
    "plugins": [
      { "name": "typescript-plugin-css-modules" }
    ]
  }
}

Estos plugins pueden añadir soporte para:

  • Importación de archivos no JavaScript (CSS, SVG, etc.)
  • Resolución dinámica basada en reglas personalizadas
  • Integración con sistemas de módulos personalizados

Depuración de problemas de resolución

Cuando enfrentamos problemas de resolución de módulos, podemos usar la opción --traceResolution para ver el proceso completo:

tsc --traceResolution

Esta opción muestra información detallada sobre:

  • Archivos buscados durante la resolución
  • Estrategias aplicadas para cada importación
  • Razones de fallo cuando no se encuentra un módulo

Optimización de la resolución de módulos

Para mejorar el rendimiento de la resolución, podemos:

  • Usar la opción "moduleDetection": "force" para tratar todos los archivos como módulos
  • Configurar "skipLibCheck": true para omitir la verificación de archivos de declaración de bibliotecas
  • Utilizar "typeRoots" para limitar dónde buscar definiciones de tipos
{
  "compilerOptions": {
    "moduleDetection": "force",
    "skipLibCheck": true,
    "typeRoots": ["./node_modules/@types", "./tipos"]
  }
}

Resolución en diferentes entornos

La configuración óptima varía según el entorno de destino:

  • Navegador con bundlers (Webpack, Rollup):
  {
    "moduleResolution": "node",
    "module": "esnext"
  }
  • Node.js moderno (con ESM):
  {
    "moduleResolution": "nodenext",
    "module": "nodenext"
  }
  • Deno:
  {
    "moduleResolution": "node",
    "module": "esnext",
    "lib": ["deno.ns", "deno.unstable"]
  }

La correcta configuración de la resolución de módulos es crucial para el desarrollo eficiente con TypeScript, ya que afecta directamente a la experiencia de desarrollo, el rendimiento de la compilación y la portabilidad del código entre diferentes entornos.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende TypeScript online

Ejercicios de esta lección Introducción a módulos

Evalúa tus conocimientos de esta lección Introducción a módulos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Funciones

TypeScript
Test

Reto composición de funciones

TypeScript
Código

Reto tipos especiales

TypeScript
Código

Reto tipos genéricos

TypeScript
Código

Módulos

TypeScript
Test

Polimorfismo

TypeScript
Código

Funciones TypeScript

TypeScript
Código

Interfaces

TypeScript
Puzzle

Funciones puras

TypeScript
Puzzle

Reto namespaces

TypeScript
Código

Funciones flecha

TypeScript
Puzzle

Polimorfismo

TypeScript
Test

Operadores

TypeScript
Test

Conversor de unidades

TypeScript
Proyecto

Funciones flecha

TypeScript
Test

Control de flujo

TypeScript
Código

Herencia

TypeScript
Puzzle

Clases

TypeScript
Puzzle

Proyecto validación de tipado

TypeScript
Proyecto

Clases y objetos

TypeScript
Código

Encapsulación

TypeScript
Test

Herencia

TypeScript
Test

Proyecto sistema de votación

TypeScript
Proyecto

Reto genéricos con clases

TypeScript
Código

Inmutabilidad

TypeScript
Puzzle

Interfaces

TypeScript
Test

Funciones de alto orden

TypeScript
Test

Reto map y filter

TypeScript
Código

Control de flujo

TypeScript
Test

Interfaces

TypeScript
Código

Reto funciones orden superior

TypeScript
Código

Herencia y clases abstractas

TypeScript
Código

Reto tipos mapped

TypeScript
Código

Herencia de clases

TypeScript
Código

Reto funciones puras

TypeScript
Código

Variables y constantes

TypeScript
Puzzle

Introducción a TypeScript

TypeScript
Test

Reto testing unitario

TypeScript
Código

Funciones de primera clase

TypeScript
Puzzle

Clases

TypeScript
Test

OOP y CRUD en TypeScript

TypeScript
Proyecto

Interfaces y su implementación

TypeScript
Código

Tipos genéricos

TypeScript
Test

Namespaces

TypeScript
Test

Proyecto calculadora gastos

TypeScript
Proyecto

Operadores y expresiones

TypeScript
Código

Proyecto generador de contraseñas

TypeScript
Proyecto

Reto unión e intersección

TypeScript
Código

Encapsulación

TypeScript
Puzzle

Tipos de unión e intersección

TypeScript
Test

Tipos de unión e intersección

TypeScript
Puzzle

Reto hola mundo en TS

TypeScript
Código

Variables y constantes

TypeScript
Código

Funciones puras

TypeScript
Test

Control de flujo

TypeScript
Código

Introducción a TypeScript

TypeScript
Código

Resolución de módulos

TypeScript
Test

Control de flujo

TypeScript
Puzzle

Reto tipos de utilidad

TypeScript
Código

Reto tipos literales y condicionales

TypeScript
Código

Reto exportar e importar

TypeScript
Código

Propiedades y métodos

TypeScript
Código

Tipos de utilidad

TypeScript
Test

Clases y objetos

TypeScript
Código

Tipos de datos, variables y constantes

TypeScript
Código

Proyecto Minigestor de tareas

TypeScript
Proyecto

Operadores

TypeScript
Puzzle

Funciones flecha y contexto

TypeScript
Código

Funciones

TypeScript
Puzzle

Reto type aliases

TypeScript
Código

Funciones de alto orden

TypeScript
Puzzle

Funciones y parámetros tipados

TypeScript
Código

Tipos literales

TypeScript
Puzzle

Reto enums

TypeScript
Código

Tipos de utilidad

TypeScript
Puzzle

Modificadores de acceso y encapsulación

TypeScript
Código

Polimorfismo

TypeScript
Puzzle

Tipos genéricos

TypeScript
Puzzle

Reto módulos

TypeScript
Código

Tipos literales

TypeScript
Test

Inmutabilidad

TypeScript
Test

Proyecto Generator de datos

TypeScript
Proyecto

Variables y constantes

TypeScript
Test

Funciones de primera clase

TypeScript
Test

Todas las lecciones de TypeScript

Accede a todas las lecciones de TypeScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Typescript

TypeScript

Introducción Y Entorno

Instalación Y Configuración De Typescript

TypeScript

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

TypeScript

Sintaxis

Operadores Y Expresiones

TypeScript

Sintaxis

Control De Flujo

TypeScript

Sintaxis

Funciones Y Parámetros Tipados

TypeScript

Sintaxis

Funciones Flecha Y Contexto

TypeScript

Sintaxis

Enums

TypeScript

Sintaxis

Type Aliases Y Aserciones De Tipo

TypeScript

Sintaxis

Clases Y Objetos

TypeScript

Programación Orientada A Objetos

Interfaces Y Su Implementación

TypeScript

Programación Orientada A Objetos

Modificadores De Acceso Y Encapsulación

TypeScript

Programación Orientada A Objetos

Herencia Y Clases Abstractas

TypeScript

Programación Orientada A Objetos

Polimorfismo

TypeScript

Programación Orientada A Objetos

Decoradores Básicos

TypeScript

Programación Orientada A Objetos

Propiedades Y Métodos

TypeScript

Programación Orientada A Objetos

Inmutabilidad

TypeScript

Programación Funcional

Funciones Puras

TypeScript

Programación Funcional

Funciones De Primera Clase

TypeScript

Programación Funcional

Funciones De Alto Orden

TypeScript

Programación Funcional

Conceptos Básicos E Inmutabilidad

TypeScript

Programación Funcional

Funciones De Primera Clase Y Orden Superior

TypeScript

Programación Funcional

Composición De Funciones

TypeScript

Programación Funcional

Métodos Funcionales De Arrays (Map, Filter, Reduce)

TypeScript

Programación Funcional

Tipos Literales

TypeScript

Tipos Intermedios Y Avanzados

Tipos Genéricos

TypeScript

Tipos Intermedios Y Avanzados

Tipos De Unión E Intersección

TypeScript

Tipos Intermedios Y Avanzados

Tipos De Utilidad

TypeScript

Tipos Intermedios Y Avanzados

Unknown, Never Y Tipos Especiales

TypeScript

Tipos Intermedios Y Avanzados

Tipos Mapped

TypeScript

Tipos Intermedios Y Avanzados

Genéricos Con Clases E Interfaces

TypeScript

Tipos Intermedios Y Avanzados

Módulos

TypeScript

Namespaces Y Módulos

Namespaces

TypeScript

Namespaces Y Módulos

Resolución De Módulos

TypeScript

Namespaces Y Módulos

Exportación E Importación De Módulos

TypeScript

Namespaces Y Módulos

Introducción A Módulos

TypeScript

Namespaces Y Módulos

Testing Unitario En Typescript

TypeScript

Testing

Accede GRATIS a TypeScript y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el sistema de módulos ES6 y su integración en TypeScript.
  • Aprender a usar las declaraciones import y export, incluyendo exportaciones por defecto y reexportaciones.
  • Diferenciar entre módulos y scripts en TypeScript y sus implicaciones en el ámbito y organización del código.
  • Entender el proceso de resolución de módulos y cómo configurar rutas, alias y estrategias en tsconfig.json.
  • Conocer buenas prácticas para organizar y migrar código hacia módulos en proyectos TypeScript.