¿Por qué «Date» en TypeScript es una trampa?
\nImagina: estás escribiendo una aplicación, todo funciona perfectamente. Pero un buen día llega un reporte de error: «El usuario de Tokio ve la fecha un día antes». Abres el código y ves el clásico: let date: Date = "2024-12-01". O peor aún — any. ¿Te suena familiar?
La pregunta «How do I express a date type in TypeScript?» en Stack Overflow ha acumulado decenas de miles de visitas. Y con razón. Parecería que la respuesta es simple: usa Date. Pero en la práctica es más complicado. TypeScript es una extensión de JavaScript, y Date en JS es un objeto que se comporta... de manera peculiar.
En este artículo no solo analizaremos la sintaxis, sino el tipado correcto de fechas, que salvará tu proyecto de errores, mejorará el rendimiento y hará que el código sea comprensible para el equipo. Aprenderás a trabajar con fechas reales en TypeScript sin perder tiempo en depuración.
\n\n1. Tipado básico: ¿Date, string o number?
\nLa opción más obvia es el tipo incorporado Date:
let birthday: Date = new Date('1990-05-15');\n¿Funciona? Sí. Pero hay un detalle: TypeScript no verifica si la fecha es válida. Puedes escribir new Date('abc') — no habrá error de compilación, pero en tiempo de ejecución obtendrás Invalid Date. Y al intentar llamar a .getTime() devolverá NaN.
Una opción más estricta es usar cadenas en formato ISO 8601:
\ntype ISODateString = string;\nlet eventDate: ISODateString = '2024-12-01T10:00:00Z';\nVentaja: tipo simple, fácil de serializar a JSON. Desventaja: sin validación — puedes asignar 'hello' y TS no dirá nada.
La tercera opción es timestamp (number):
\ntype Timestamp = number;\nlet ms: Timestamp = Date.now();\nIdeal para operaciones matemáticas, pero ilegible para humanos. Y de nuevo — cualquier número pasará la verificación.
\n\nResumen de tipos básicos
\nSi necesitas un tipado rápido y sencillo — usa Date. Si trabajas con API — string. Si solo son cálculos — number. Pero para proyectos serios esto no es suficiente. Profundicemos.
2. Tipos personalizados y branded types
\nPara que TypeScript no confunda una cadena de fecha con una cadena normal, se usan branded types. Es como una etiqueta que dice «Soy una fecha, ¡verificado!»:
\ntype DateString = string & { __brand: 'DateString' };\n\nfunction toDateString(date: Date): DateString {\n return date.toISOString() as DateString;\n}\n\nfunction processDate(d: DateString) {\n // aquí d es definitivamente una cadena ISO\n}\n\nconst iso = toDateString(new Date());\nprocessDate(iso); // OK\n// processDate('hello'); // ❌ ¡Error!\nEsto agrega seguridad en tiempo de compilación. El costo es un poco más de código. Pero para proyectos grandes (especialmente si tienes microservicios o API REST) es el estándar de oro.
\n\n3. Librerías para trabajar con fechas en TypeScript
\nEl Date nativo es malo. Es mutable, analiza cadenas de forma inconsistente, y las zonas horarias son un dolor aparte. Por eso en proyectos reales se usan librerías. Aquí hay tres favoritas:
3.1. date-fns (ligera y modular)
\nimport { format, parseISO } from 'date-fns';\n\nconst date: Date = parseISO('2024-12-01T10:00:00Z');\nconst formatted: string = format(date, 'dd.MM.yyyy');\nVentaja: tree-shaking, inmutable (devuelve nuevas fechas), excelente tipado.
\n\n3.2. Day.js (minimalista)
\nimport dayjs, { Dayjs } from 'dayjs';\n\nconst now: Dayjs = dayjs();\nconst formatted: string = now.format('DD.MM.YYYY');\nDay.js es como Moment.js, pero sin el bundle pesado. El tipo Dayjs es un envoltorio que es fácil de pasar entre funciones.