Módulos, import y export en TypeScript

Intermedio
TypeScript
TypeScript
Actualizado: 18/04/2026

Sistema de módulos ES en TypeScript

En TypeScript, cualquier archivo que contenga al menos una declaración import o export en el nivel superior se considera un módulo. Los archivos sin estas declaraciones se tratan como scripts, cuyo contenido está disponible en el ámbito global.

Sistema de módulos ES en TypeScript

// matematicas.ts - Este archivo ES un módulo (tiene export)
export function sumar(a: number, b: number): number {
    return a + b
}

// Los valores no exportados son privados al módulo
const PI = 3.14159

export function areaCirculo(radio: number): number {
    return PI * radio * radio
}

Los módulos tienen su propio ámbito léxico: las variables, funciones, clases y tipos declarados en un módulo no están disponibles fuera de el a menos que se exporten explícitamente.

Si un archivo no tiene import ni export pero se quiere tratar como módulo, se puede añadir export {} al final. Esto convierte el archivo en un módulo sin exportar nada.

Módulos vs scripts

La diferencia fundamental entre módulos y scripts es el ámbito de las declaraciones:

// script1.ts (sin import/export, es un script)
const MAX = 100
function inicializar() { /* ... */ }

// script2.ts (otro script)
// const MAX = 200 // Error: identificador duplicado en ámbito global
// modulo1.ts
export const MAX = 100
export function inicializar() { /* ... */ }

// modulo2.ts
export const MAX = 200 // Sin error: ambitos separados
export function inicializar() { /* ... */ }

Exportaciones con nombre

Las exportaciones con nombre (named exports) permiten exponer múltiples valores desde un módulo. Cada valor exportado mantiene su nombre original:

// logger.ts
export const NIVELES = {
    INFO: "info",
    WARN: "warn",
    ERROR: "error"
} as const

export interface MensajeLog {
    nivel: string
    mensaje: string
    timestamp: Date
}

export class Logger {
    registrar(nivel: string, mensaje: string): void {
        console.log(`[${nivel.toUpperCase()}]: ${mensaje}`)
    }
}

export function formatearMensaje(msg: MensajeLog): string {
    return `[${msg.nivel}][${msg.timestamp.toISOString()}] ${msg.mensaje}`
}

Existe una sintaxis alternativa que agrupa las exportaciones al final del archivo:

// utilidades.ts
const VERSION = "1.0.0"

function formatearFecha(fecha: Date): string {
    return fecha.toISOString().split("T")[0]
}

interface Resultado<T> {
    datos: T
    exitoso: boolean
}

export { VERSION, formatearFecha, Resultado }

Renombrar al exportar

Se pueden renombrar los elementos durante la exportación con la palabra clave as:

// config.ts
const claveApi = "abc123"
const urlApi = "https://api.ejemplo.com"

export {
    claveApi as API_KEY,
    urlApi as API_URL
}

Importaciones con nombre

Para consumir exportaciones con nombre se usa la sintaxis de desestructuración:

// app.ts
import { Logger, NIVELES, MensajeLog } from "./logger"

const logger = new Logger()
logger.registrar(NIVELES.INFO, "Aplicación iniciada")

const mensaje: MensajeLog = {
    nivel: NIVELES.WARN,
    mensaje: "Recurso no encontrado",
    timestamp: new Date()
}

Importación selectiva

Se pueden importar solo los elementos necesarios, lo que facilita el tree-shaking en bundlers:

import { sumar } from "./matematicas"

console.log(sumar(10, 5))

Renombrar al importar

La palabra clave as permite renombrar elementos durante la importación para evitar colisiones:

import { Logger as ServicioLog } from "./logger"
import { Logger as LoggerArchivo } from "./logger-archivo"

const logConsola = new ServicioLog()
const logArchivo = new LoggerArchivo()

Importar todo el módulo

El operador * as importa todas las exportaciones como un objeto namespace:

import * as matematicas from "./matematicas"

console.log(matematicas.sumar(5, 3))
console.log(matematicas.areaCirculo(10))

Exportaciones por defecto

Cada módulo puede tener una única exportación por defecto. Se usa cuando el módulo tiene un propósito principal claro:

// cliente-api.ts
export default class ClienteAPI {
    constructor(private urlBase: string) {}

    async obtener<T>(endpoint: string): Promise<T> {
        const respuesta = await fetch(`${this.urlBase}/${endpoint}`)
        return respuesta.json()
    }

    async enviar<T>(endpoint: string, datos: unknown): Promise<T> {
        const respuesta = await fetch(`${this.urlBase}/${endpoint}`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(datos)
        })
        return respuesta.json()
    }
}

Al importar, no se usan llaves y se puede asignar cualquier nombre:

import ClienteAPI from "./cliente-api"
// o: import API from "./cliente-api"

const api = new ClienteAPI("https://api.ejemplo.com")

Combinar exportación por defecto y con nombre

Un módulo puede tener ambos tipos de exportación simultáneamente:

// validación.ts
export function esEmail(valor: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor)
}

export function esNumero(valor: string): boolean {
    return !isNaN(Number(valor))
}

export default class Validador {
    validar(
        esquema: Record<string, (valor: string) => boolean>,
        datos: Record<string, string>
    ): boolean {
        for (const [campo, validar] of Object.entries(esquema)) {
            if (!validar(datos[campo])) return false
        }
        return true
    }
}
import Validador, { esEmail, esNumero } from "./validación"

const validador = new Validador()
const resultado = validador.validar(
    { email: esEmail, edad: esNumero },
    { email: "ana@ejemplo.com", edad: "28" }
)

Importaciones de solo tipo

TypeScript permite importar exclusivamente tipos con la sintaxis import type. Estas importaciones se eliminan completamente del JavaScript generado:

// tipos.ts
export interface Usuario {
    id: string
    nombre: string
    email: string
}

export type IdUsuario = string

export enum RolUsuario {
    Admin = "admin",
    Editor = "editor",
    Lector = "lector"
}
// servicio-usuario.ts
import type { Usuario, IdUsuario } from "./tipos"
import { RolUsuario } from "./tipos"

// Usuario e IdUsuario solo se usan como tipos, no generan JavaScript
function obtenerUsuario(id: IdUsuario): Promise<Usuario> {
    return fetch(`/api/usuarios/${id}`).then(r => r.json())
}

// RolUsuario se importa normalmente porque se usa como valor
const rolPorDefecto = RolUsuario.Lector

También se puede marcar cada importación individual con type:

import { type Usuario, type IdUsuario, RolUsuario } from "./tipos"

Las importaciones de solo tipo son esenciales cuando se usan transpiladores como Babel, swc o esbuild, que no ejecutan el compilador de TypeScript completo. Permiten a estos transpiladores saber que importaciones pueden eliminar de forma segura.

Export type

De forma analoga, se pueden reexportar solo tipos:

export type { Usuario, IdUsuario } from "./tipos"

Reexportaciones

Las reexportaciones permiten crear módulos intermediarios que agrupan exportaciones de otros módulos sin importarlas localmente:

// modelos/usuario.ts
export interface Usuario {
    id: string
    nombre: string
}

// modelos/producto.ts
export interface Producto {
    id: string
    titulo: string
    precio: number
}

// modelos/index.ts - Reexportacion
export { Usuario } from "./usuario"
export { Producto } from "./producto"

Reexportar todo

El operador * reexporta todas las exportaciones con nombre:

// modelos/index.ts
export * from "./usuario"
export * from "./producto"

Reexportar exportación por defecto

La exportación por defecto se puede reexportar como exportación con nombre:

// componentes/index.ts
export { default as Boton } from "./Boton"
export { default as Input } from "./Input"
export { default as Selector } from "./Selector"

Renombrar al reexportar

// api/index.ts
export { validar as validarEmail } from "./validador-email"
export { validar as validarPassword } from "./validador-password"

Barrel files (index.ts)

El patrón barrel consiste en crear un archivo index.ts que reexporta los elementos de una carpeta, simplificando las importaciones:

src/
  modelos/
    usuario.ts
    producto.ts
    pedido.ts
    index.ts      <-- barrel file
  servicios/
    auth.ts
    api.ts
    index.ts      <-- barrel file
// modelos/index.ts
export * from "./usuario"
export * from "./producto"
export * from "./pedido"

Los consumidores importan directamente desde la carpeta:

// Sin barrel file
import { Usuario } from "./modelos/usuario"
import { Producto } from "./modelos/producto"
import { Pedido } from "./modelos/pedido"

// Con barrel file
import { Usuario, Producto, Pedido } from "./modelos"

Fachada de API pública

Los barrel files permiten ocultar la estructura interna exponiendo solo una API pública:

// servicios/interno/usuarios.ts
export function obtenerTodos() { /* ... */ }
export function obtenerPorId(id: string) { /* ... */ }
export function _validacionInterna() { /* ... */ }

// servicios/index.ts - Solo expone la API pública
export { obtenerTodos, obtenerPorId } from "./interno/usuarios"
// No reexporta _validacionInterna

Consideraciones de rendimiento

Las reexportaciones con export * pueden dificultar el tree-shaking en algunos bundlers. Para módulos grandes, las reexportaciones selectivas son preferibles:

// Mejor para tree-shaking
export { funcion1, funcion2 } from "./módulo"

// Puede dificultar tree-shaking
export * from "./módulo"

Importaciones dinamicas

La función import() permite cargar módulos de forma asíncrona, útil para code splitting:

async function cargarModuloMatematicas() {
    const matematicas = await import("./matematicas")
    console.log(matematicas.sumar(5, 3))
}

// O con desestructuracion
async function procesarDatos() {
    const { formatearFecha } = await import("./utilidades")
    console.log(formatearFecha(new Date()))
}

TypeScript infiere correctamente los tipos de las importaciones dinamicas, proporcionando autocompletado y verificación de tipos incluso en módulos cargados de forma diferida.

Organización de módulos en proyectos

Una estructura recomendada para proyectos TypeScript:

src/
  tipos/           # Tipos e interfaces compartidos
    index.ts
  modelos/         # Modelos de dominio
    index.ts
  servicios/       # Logica de negocio
    index.ts
  utilidades/      # Funciones auxiliares
    index.ts
  index.ts         # Punto de entrada principal

Cada carpeta contiene su barrel file y expone una API pública limpia. Las dependencias entre módulos fluyen en una dirección clara, facilitando el mantenimiento y la comprensión del código.

// src/index.ts - Punto de entrada principal de una librería
export type { Usuario, Producto } from "./tipos"
export { ServicioUsuarios } from "./servicios"
export { formatearMoneda, formatearFecha } from "./utilidades"

Este enfoque garantiza que los consumidores del módulo tengan acceso a una API bien definida sin necesidad de conocer la estructura interna del proyecto.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de TypeScript

Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender el sistema de módulos ES en TypeScript, dominar las exportaciones con nombre y por defecto, usar reexportaciones y barrel files, aplicar importaciones de solo tipo con import type, y organizar módulos en proyectos reales.