2
0

axios.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // eslint-disable-next-line no-restricted-imports
  2. import axios from 'axios';
  3. import dayjs from 'dayjs';
  4. import qs from 'qs';
  5. // eslint-disable-next-line no-restricted-imports
  6. export * from 'axios';
  7. const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(Z|[+-]\d{2}:\d{2})$/;
  8. // For type checking for Date and String types to work
  9. export type IsoDateString = string & { readonly __isIsoDateString: unique symbol };
  10. type IsTuple<T> = T extends readonly any[]
  11. ? number extends T['length']
  12. ? false // Not a tuple (it's a regular array)
  13. : true // It's a tuple
  14. : false;
  15. // Utility type to decide the resulting type of every input object at compile time.
  16. type DeepDateConvert<T> = T extends Date
  17. ? T
  18. : T extends IsoDateString
  19. ? Date
  20. : T extends string
  21. ? T
  22. : IsTuple<T> extends true
  23. ? { [K in keyof T]: DeepDateConvert<T[K]> }
  24. : T extends (infer U)[]
  25. ? DeepDateConvert<U>[]
  26. : T extends object
  27. ? { [K in keyof T]: DeepDateConvert<T[K]> }
  28. : T;
  29. /**
  30. * Converts string to dates recursively.
  31. *
  32. * @param data - Data to be transformed to Date if applicable.
  33. * @param seen - Set containing data that has been through the function before.
  34. * @returns - Data containing transformed Dates.
  35. */
  36. function convertStringsToDatesRecursive<T>(data: T, seen: Set<any>): DeepDateConvert<T> {
  37. if (typeof data !== 'object' || data === null) {
  38. if (typeof data === 'string' && isoDateRegex.test(data)) {
  39. return new Date(data) as DeepDateConvert<T>;
  40. }
  41. return data as DeepDateConvert<T>;
  42. }
  43. // Check for circular reference
  44. if (seen.has(data)) {
  45. return data as DeepDateConvert<T>;
  46. }
  47. seen.add(data);
  48. if (Array.isArray(data)) {
  49. const resultArray = (data as any[]).map(array => convertStringsToDatesRecursive(array, seen));
  50. return resultArray as DeepDateConvert<T>;
  51. }
  52. const newData: { [key: string]: any } = {};
  53. for (const key of Object.keys(data as object)) {
  54. const value = (data as any)[key];
  55. if (typeof value === 'string' && isoDateRegex.test(value)) {
  56. newData[key] = new Date(value);
  57. }
  58. else if (typeof value === 'object' && value !== null) {
  59. newData[key] = convertStringsToDatesRecursive(value, seen);
  60. }
  61. else {
  62. newData[key] = value;
  63. }
  64. }
  65. return newData as DeepDateConvert<T>;
  66. }
  67. export function convertStringsToDates<T>(data: T): DeepDateConvert<T> {
  68. return convertStringsToDatesRecursive(data, new Set());
  69. }
  70. // Determine the base array of transformers
  71. let baseTransformers = axios.defaults.transformResponse;
  72. if (baseTransformers == null) {
  73. baseTransformers = [];
  74. }
  75. else if (!Array.isArray(baseTransformers)) {
  76. // If it's a single transformer function, wrap it in an array
  77. baseTransformers = [baseTransformers];
  78. }
  79. const customAxios = axios.create({
  80. headers: {
  81. 'X-Requested-With': 'XMLHttpRequest',
  82. 'Content-Type': 'application/json',
  83. },
  84. transformResponse: baseTransformers.concat(
  85. (data) => {
  86. return convertStringsToDates(data);
  87. },
  88. ),
  89. });
  90. // serialize Date config: https://github.com/axios/axios/issues/1548#issuecomment-548306666
  91. customAxios.interceptors.request.use((config) => {
  92. config.paramsSerializer = params => qs.stringify(params, { serializeDate: (date: Date) => dayjs(date).format('YYYY-MM-DDTHH:mm:ssZ') });
  93. return config;
  94. });
  95. export default customAxios;