Back

Cómo Organizar las Definiciones de Tipos en un Proyecto TypeScript

Cómo Organizar las Definiciones de Tipos en un Proyecto TypeScript

La mayoría de los proyectos de TypeScript comienzan de la misma manera: un único archivo types.ts que lentamente crece hasta cientos de líneas. Encontrar algo se convierte en una tarea ardua. Reutilizar tipos entre archivos se siente arriesgado. Los nuevos miembros del equipo no saben dónde buscar.

La buena noticia es que organizar los tipos de TypeScript no requiere un sistema complejo. Requiere una regla clara: coloca los tipos lo más cerca posible de donde se usan, y muévelos solo cuando se necesiten en otro lugar.

Aquí te mostramos cómo aplicar esa regla en cada etapa de un proyecto.

Puntos Clave

  • Coloca los tipos lo más cerca posible de su uso y promuévelos a ubicaciones compartidas solo cuando múltiples archivos los necesiten.
  • Usa archivos .ts para exportaciones e importaciones explícitas de tipos; reserva los archivos .d.ts estrictamente para declaraciones ambientales como variables de entorno o aumentos globales.
  • Adopta una convención de nomenclatura consistente como *.types.ts para tipos específicos de módulos y archivos barrel index.ts para rutas de importación limpias y estables.
  • En monorepos o paquetes compartidos, expón los tipos a través de la condición types en las exportaciones de package.json y mantén los tipos internos privados.

La Decisión Principal: ¿Dónde Debe Vivir un Tipo?

Hazte una pregunta: ¿cuántos archivos necesitan este tipo?

  • Un archivo → defínelo inline en ese archivo
  • Unos pocos archivos en el mismo módulo → colócalo en un archivo .types.ts compartido cercano
  • A través de todo el proyecto → muévelo a un directorio compartido src/types/
  • A través de paquetes → publícalo desde un paquete de tipos dedicado

Esta progresión mantiene las definiciones de tipos de tu proyecto organizadas sin sobre-ingeniería desde el inicio.

Patrón 1: Tipos Inline para Uso Local

Si un tipo solo se usa en un archivo, defínelo allí. No se necesita un archivo separado.

// UserCard.tsx
interface UserCardProps {
  name: string
  avatarUrl: string
  role: "admin" | "viewer"
}

export function UserCard({ name, avatarUrl, role }: UserCardProps) {
  // ...
}

Este es el valor predeterminado correcto. La abstracción prematura crea indirección sin beneficio.

Patrón 2: Archivos de Tipos Colocados para Tipos Compartidos de Módulos

Cuando dos o más componentes en la misma carpeta comparten tipos, extráelos a un archivo .types.ts colocado.

src/components/user/
├── UserCard.tsx
├── UserList.tsx
└── user.types.ts       ← tipos compartidos para este módulo

Esto mantiene los tipos relacionados cerca del código que los usa sin contaminar un archivo global.

Patrón 3: Un Directorio types/ Compartido para Definiciones Transversales

Cuando los tipos se usan en múltiples módulos no relacionados, un directorio central src/types/ tiene sentido. Usa un archivo barrel index.ts para mantener las importaciones limpias y estables.

src/types/
├── index.ts            ← re-exporta todo
├── api.types.ts
├── user.types.ts
└── product.types.ts
// src/types/index.ts
export type { ApiResponse, PaginatedResult } from "./api.types"
export type { User, UserRole } from "./user.types"

Los consumidores importan desde una sola ruta: import type { User } from "@/types". Si más tarde reorganizas los internos, las rutas de importación no cambian.

¿Deberías Usar .ts o .d.ts para Definiciones de Tipos?

Este es uno de los puntos de confusión más comunes en la organización de tipos de TypeScript.

Usa archivos .ts para tipos que exportas e importas explícitamente. Esta es la elección correcta para casi todo en una aplicación.

Usa archivos .d.ts principalmente para declaraciones ambientales — situaciones donde necesitas decirle a TypeScript sobre algo que existe en tiempo de ejecución pero no tiene fuente TypeScript. Ejemplos comunes:

  • env.d.ts — declarando variables import.meta.env para Vite o bundlers similares
  • global.d.ts — aumentando tipos de módulos de terceros o declarando variables globales
// env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
  readonly VITE_API_URL: string
}

Algunos proyectos modernos configuran compilerOptions.types explícitamente para controlar qué tipos ambientales se cargan, mientras que otros dependen del descubrimiento automático de @types. Si estás usando una cadena de herramientas basada en bundler como Vite o Next.js, sigue sus convenciones recomendadas para estos archivos en lugar de crear declaraciones globales independientes.

Convenciones de Nomenclatura que Escalan

La nomenclatura consistente reduce la carga cognitiva en un equipo:

PatrónEjemploCaso de uso
*.types.tsuser.types.tsTipos específicos de módulos
index.tstypes/index.tsRe-exportación barrel
env.d.tsenv.d.tsDeclaraciones de variables de entorno
global.d.tsglobal.d.tsAumento de tipos de terceros

Evita prefijos IUser y sufijos UserType. Nombres simples como User y ApiResponse son más limpios y se alinean con las convenciones modernas de TypeScript.

Organizando Tipos de TypeScript en un Paquete Compartido

Si estás construyendo una biblioteca o trabajando en un monorepo, expón los tipos a través de las exportaciones de package.json usando la condición types en lugar de depender de patrones antiguos de typesVersions.

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  }
}

Mantén los tipos internos privados — no los exportes desde el punto de entrada del paquete. Solo la superficie de la API pública debe ser visible para los consumidores.

Conclusión

Comienza con tipos inline. Extrae a un archivo colocado cuando un segundo archivo los necesite. Promueve a src/types/ cuando sean verdaderamente transversales. Usa archivos .d.ts solo para declaraciones ambientales, no como un hogar predeterminado para todas las definiciones de tipos.

El objetivo de organizar los tipos de TypeScript no es una estructura de carpetas perfecta — es hacer que el próximo desarrollador (o tu yo futuro) pueda encontrar y reutilizar tipos sin fricción.

Preguntas Frecuentes

No. Un único archivo funciona al principio pero se vuelve difícil de navegar a medida que el proyecto crece. En su lugar, mantén los tipos cerca de donde se usan. Defínelos inline para uso en un solo archivo, extrae a un archivo .types.ts colocado cuando se compartan dentro de un módulo, y solo promueve a un directorio de tipos central cuando se necesiten en partes no relacionadas del código.

Usa archivos .ts para tipos que exportas e importas explícitamente en el código de tu aplicación. Usa archivos .d.ts solo para declaraciones ambientales, como describir variables de entorno o aumentar tipos de módulos de terceros. Colocar tipos regulares de aplicación en archivos .d.ts puede causar confusión porque esas declaraciones están disponibles globalmente sin una importación explícita.

Una convención ampliamente adoptada es usar el patrón nombre-modulo.types.ts para tipos específicos de módulos y un archivo barrel index.ts para re-exportaciones en un directorio de tipos compartido. Evita la notación húngara como IUser o sufijos redundantes como UserType. Nombres simples y descriptivos como User y ApiResponse son preferidos en proyectos modernos de TypeScript.

Usa la condición types dentro del campo exports de tu package.json para apuntar a tu archivo de declaración compilado. Este enfoque es más explícito y confiable que el patrón antiguo de typesVersions. Solo exporta tipos que forman parte de tu API pública y mantén los tipos internos privados para evitar filtrar detalles de implementación a los consumidores.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay