Pourquoi « Date » en TypeScript est un piège ?
\nImaginez : vous écrivez une application, tout fonctionne parfaitement. Mais un beau jour, un rapport de bug arrive : « L'utilisateur de Tokyo voit la date un jour plus tôt ». Vous ouvrez le code et voyez le classique : let date: Date = "2024-12-01". Ou pire encore — any. Cela vous dit quelque chose ?
La question « How do I express a date type in TypeScript? » sur Stack Overflow a recueilli des dizaines de milliers de vues. Et ce n'est pas pour rien. La réponse semble simple : utilisez Date. Mais en pratique, c'est plus compliqué. TypeScript est une surcouche de JavaScript, et Date en JS est un objet qui se comporte... de manière particulière.
Dans cet article, nous n'allons pas simplement aborder la syntaxe, mais le typage correct des dates, qui sauvera votre projet des bugs, améliorera les performances et rendra le code compréhensible pour l'équipe. Vous apprendrez à travailler avec des dates réelles en TypeScript, sans perdre de temps en débogage.
\n\n1. Typage de base : Date, string ou number ?
\nL'option la plus évidente est le type natif Date :
let birthday: Date = new Date('1990-05-15');\nÇa marche ? Oui. Mais il y a un détail : TypeScript ne vérifie pas si la date est valide. Vous pouvez écrire new Date('abc') — il n'y aura pas d'erreur de compilation, mais à l'exécution vous obtiendrez Invalid Date. Et essayer d'appeler .getTime() retournera NaN.
Une option plus stricte consiste à utiliser des chaînes au format ISO 8601 :
\ntype ISODateString = string;\nlet eventDate: ISODateString = '2024-12-01T10:00:00Z';\nAvantage : type simple, facile à sérialiser en JSON. Inconvénient : aucune validation — vous pouvez assigner 'hello', et TS ne dira rien.
Troisième option — timestamp (number) :
\ntype Timestamp = number;\nlet ms: Timestamp = Date.now();\nIdéal pour les opérations mathématiques, mais illisible pour un humain. Et encore une fois — n'importe quel nombre passera la vérification.
\n\nRésumé des types de base
\nSi vous avez besoin d'un typage rapide et approximatif — utilisez Date. Si vous travaillez avec une API — string. Si ce ne sont que des calculs — number. Mais pour les projets sérieux, cela ne suffit pas. Approfondissons.
2. Types personnalisés et typage par marque (branded types)
\nPour que TypeScript ne confonde pas une chaîne de date avec une chaîne ordinaire, on utilise les types marqués (branded types). C'est comme une étiquette « Je suis une date, vérifié ! » :
\ntype DateString = string & { __brand: 'DateString' };\n\nfunction toDateString(date: Date): DateString {\n return date.toISOString() as DateString;\n}\n\nfunction processDate(d: DateString) {\n // ici d est bien une chaîne ISO\n}\n\nconst iso = toDateString(new Date());\nprocessDate(iso); // OK\n// processDate('hello'); // ❌ Erreur !\nCela ajoute de la sécurité au moment de la compilation. Le prix : un peu plus de code. Mais pour les grands projets (surtout si vous avez des microservices ou une API REST), c'est le standard en or.
\n\n3. Bibliothèques pour travailler avec les dates en TypeScript
\nLe Date brut est un mal. Il est mutable, parse les chaînes de manière incohérente, et les fuseaux horaires sont une douleur particulière. C'est pourquoi dans les projets réels, on utilise des bibliothèques. Voici trois favorites :
3.1. date-fns (légère et modulaire)
\nimport { format, parseISO } from 'date-fns';\n\nconst date: Date = parseISO('2024-12-01T10:00:00Z');\nconst formatted: string = format(date, 'dd.MM.yyyy');\nAvantage : tree-shaking, immutable (retourne de nouvelles dates), excellent typage.
\n\n3.2. Day.js (minimaliste)
\nimport dayjs, { Dayjs } from 'dayjs';\n\nconst now: Dayjs = dayjs();\nconst formatted: string = now.format('DD.MM.YYYY');\nDay.js est comme Moment.js, mais sans le bundle volumineux. Le type Dayjs est une enveloppe facile à passer entre les fonctions.