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
.tspara exportaciones e importaciones explícitas de tipos; reserva los archivos.d.tsestrictamente para declaraciones ambientales como variables de entorno o aumentos globales. - Adopta una convención de nomenclatura consistente como
*.types.tspara tipos específicos de módulos y archivos barrelindex.tspara rutas de importación limpias y estables. - En monorepos o paquetes compartidos, expón los tipos a través de la condición
typesen las exportaciones depackage.jsony 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.tscompartido 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.
Discover how at OpenReplay.com.
¿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 variablesimport.meta.envpara Vite o bundlers similaresglobal.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ón | Ejemplo | Caso de uso |
|---|---|---|
*.types.ts | user.types.ts | Tipos específicos de módulos |
index.ts | types/index.ts | Re-exportación barrel |
env.d.ts | env.d.ts | Declaraciones de variables de entorno |
global.d.ts | global.d.ts | Aumento 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.