arvid-e 9 месяцев назад
Родитель
Сommit
93538c24d3
2 измененных файлов с 33 добавлено и 48 удалено
  1. 12 13
      apps/app/src/utils/axios-date-conversion.spec.ts
  2. 21 35
      apps/app/src/utils/axios.ts

+ 12 - 13
apps/app/src/utils/axios-date-conversion.spec.ts

@@ -1,5 +1,4 @@
 import { convertStringsToDates } from './axios';
-import type { IsoDateString } from './axios';
 
 
 describe('convertStringsToDates', () => {
@@ -9,7 +8,7 @@ describe('convertStringsToDates', () => {
     const dateString = '2023-01-15T10:00:00.000Z';
     const input = {
       id: 1,
-      createdAt: dateString as IsoDateString,
+      createdAt: dateString,
       name: 'Test Item',
     };
     const expected = {
@@ -30,12 +29,12 @@ describe('convertStringsToDates', () => {
     const input = {
       data: {
         item1: {
-          updatedAt: dateString1 as IsoDateString,
+          updatedAt: dateString1,
           value: 10,
         },
         item2: {
           nested: {
-            deletedAt: dateString2 as IsoDateString,
+            deletedAt: dateString2,
             isActive: false,
           },
         },
@@ -68,8 +67,8 @@ describe('convertStringsToDates', () => {
     const dateString1 = '2023-04-05T14:15:00.000Z';
     const dateString2 = '2023-05-10T16:00:00.000Z';
     const input = [
-      { id: 1, eventDate: dateString1 as IsoDateString },
-      { id: 2, eventDate: dateString2 as IsoDateString, data: { nestedProp: 'value' } },
+      { id: 1, eventDate: dateString1ng },
+      { id: 2, eventDate: dateString2ng, data: { nestedProp: 'value' } },
     ];
     const expected = [
       { id: 1, eventDate: new Date(dateString1) },
@@ -86,7 +85,7 @@ describe('convertStringsToDates', () => {
   // Test case 4: Array containing date strings directly (though less common for this function)
   test('should handle arrays containing date strings directly', () => {
     const dateString = '2023-06-20T18:00:00.000Z';
-    const input: [string, IsoDateString, number] = ['text', dateString as IsoDateString, 123];
+    const input: [string, string, number] = ['text', dateString, 123];
     const expected = ['text', new Date(dateString), 123];
     const result = convertStringsToDates(input);
     expect(result[1]).toBeInstanceOf(Date);
@@ -130,7 +129,7 @@ describe('convertStringsToDates', () => {
   // Test case 8: Date string with different milliseconds (isoDateRegex without .000)
   test('should handle date strings with varied milliseconds', () => {
     const dateString = '2023-01-15T10:00:00Z'; // No milliseconds
-    const input = { createdAt: dateString as IsoDateString };
+    const input = { createdAt: dateString };
     const expected = { createdAt: new Date(dateString) };
     const result = convertStringsToDates(input);
     expect(result.createdAt).toBeInstanceOf(Date);
@@ -168,9 +167,9 @@ describe('convertStringsToDates', () => {
     const dateStringWithOffset = '2025-06-12T14:00:00+09:00';
     const input = {
       id: 2,
-      eventTime: dateStringWithOffset as IsoDateString,
+      eventTime: dateStringWithOffsetng,
       details: {
-        lastActivity: '2025-06-12T05:00:00-04:00' as IsoDateString,
+        lastActivity: '2025-06-12T05:00:00-04:00'ng,
       },
     };
     const expected = {
@@ -195,7 +194,7 @@ describe('convertStringsToDates', () => {
   test('should convert ISO date strings with negative UTC offset (-05:00) to Date objects', () => {
     const dateStringWithNegativeOffset = '2025-01-01T10:00:00-05:00';
     const input = {
-      startTime: dateStringWithNegativeOffset as IsoDateString,
+      startTime: dateStringWithNegativeOffsetng,
     };
     const expected = {
       startTime: new Date(dateStringWithNegativeOffset),
@@ -212,7 +211,7 @@ describe('convertStringsToDates', () => {
   test('should convert ISO date strings with explicit zero UTC offset (+00:00) to Date objects', () => {
     const dateStringWithZeroOffset = '2025-03-15T12:00:00+00:00';
     const input = {
-      zeroOffsetDate: dateStringWithZeroOffset as IsoDateString,
+      zeroOffsetDate: dateStringWithZeroOffsetng,
     };
     const expected = {
       zeroOffsetDate: new Date(dateStringWithZeroOffset),
@@ -229,7 +228,7 @@ describe('convertStringsToDates', () => {
   test('should convert ISO date strings with milliseconds and UTC offset to Date objects', () => {
     const dateStringWithMsAndOffset = '2025-10-20T23:59:59.999-07:00';
     const input = {
-      detailedTime: dateStringWithMsAndOffset as IsoDateString,
+      detailedTime: dateStringWithMsAndOffsetng,
     };
     const expected = {
       detailedTime: new Date(dateStringWithMsAndOffset),

+ 21 - 35
apps/app/src/utils/axios.ts

@@ -1,6 +1,6 @@
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
-import dayjs from 'dayjs';
+import { formatISO } from 'date-fns';
 import qs from 'qs';
 
 // eslint-disable-next-line no-restricted-imports
@@ -8,29 +8,7 @@ export * from 'axios';
 
 const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(Z|[+-]\d{2}:\d{2})$/;
 
-// For type checking for Date and String types to work
-export type IsoDateString = string & { readonly __isIsoDateString: unique symbol };
-
-type IsTuple<T> = T extends readonly any[]
-    ? number extends T['length']
-        ? false // Not a tuple (it's a regular array)
-        : true // It's a tuple
-    : false;
-
-// Utility type to decide the resulting type of every input object at compile time.
-type DeepDateConvert<T> = T extends Date
-    ? T
-    : T extends IsoDateString
-    ? Date
-    : T extends string
-    ? T
-    : IsTuple<T> extends true
-    ? { [K in keyof T]: DeepDateConvert<T[K]> }
-    : T extends (infer U)[]
-    ? DeepDateConvert<U>[]
-    : T extends object
-    ? { [K in keyof T]: DeepDateConvert<T[K]> }
-    : T;
+export type DateConvertible = string | number | boolean | Date | null | undefined | DateConvertible[] | { [key: string]: DateConvertible };
 
 /**
 * Converts string to dates recursively.
@@ -39,29 +17,28 @@ type DeepDateConvert<T> = T extends Date
 * @param seen - Set containing data that has been through the function before.
 * @returns - Data containing transformed Dates.
 */
-function convertStringsToDatesRecursive<T>(data: T, seen: Set<any>): DeepDateConvert<T> {
+function convertStringsToDatesRecursive(data: DateConvertible, seen: Set<unknown>): DateConvertible {
   if (typeof data !== 'object' || data === null) {
     if (typeof data === 'string' && isoDateRegex.test(data)) {
-      return new Date(data) as DeepDateConvert<T>;
+      return new Date(data);
     }
-    return data as DeepDateConvert<T>;
+    return data;
   }
 
   // Check for circular reference
   if (seen.has(data)) {
-    return data as DeepDateConvert<T>;
+    return data;
   }
   seen.add(data);
 
   if (Array.isArray(data)) {
-    const resultArray = (data as any[]).map(array => convertStringsToDatesRecursive(array, seen));
-    return resultArray as DeepDateConvert<T>;
+    return data.map(item => convertStringsToDatesRecursive(item, seen));
   }
 
-  const newData: { [key: string]: any } = {};
+  const newData: Record<string, DateConvertible> = {};
 
   for (const key of Object.keys(data as object)) {
-    const value = (data as any)[key];
+    const value = (data as Record<string, DateConvertible>)[key];
 
     if (typeof value === 'string' && isoDateRegex.test(value)) {
       newData[key] = new Date(value);
@@ -76,10 +53,15 @@ function convertStringsToDatesRecursive<T>(data: T, seen: Set<any>): DeepDateCon
     }
   }
 
-  return newData as DeepDateConvert<T>;
+  return newData;
 }
 
-export function convertStringsToDates<T>(data: T): DeepDateConvert<T> {
+// Function overloads for better type inference
+export function convertStringsToDates(data: string): string | Date;
+export function convertStringsToDates<T extends DateConvertible>(data: T): DateConvertible;
+export function convertStringsToDates<T extends DateConvertible[]>(data: T): DateConvertible[];
+export function convertStringsToDates<T extends Record<string, DateConvertible>>(data: T): Record<string, DateConvertible>;
+export function convertStringsToDates(data: DateConvertible): DateConvertible {
   return convertStringsToDatesRecursive(data, new Set());
 }
 
@@ -111,7 +93,11 @@ const customAxios = axios.create({
 
 // serialize Date config: https://github.com/axios/axios/issues/1548#issuecomment-548306666
 customAxios.interceptors.request.use((config) => {
-  config.paramsSerializer = params => qs.stringify(params, { serializeDate: (date: Date) => dayjs(date).format('YYYY-MM-DDTHH:mm:ssZ') });
+  config.paramsSerializer = params => qs.stringify(params, {
+    serializeDate: (date: Date) => {
+      return formatISO(date, { representation: 'complete' });
+    },
+  });
   return config;
 });