Repository: Hacker0x01/react-datepicker
Pull request #4707 (2024-04-19 11:29:26)

[typescript-migration] date_utils.js
---
name: Pull Request
about: Create a pull request to improve this repository
---

## Description
**Linked issue**:  https://github.com/Hacker0x01/react-datepicker/issues/4700

## To reviewers
<!-- Additional comments for reviewers -->

An important topic to discuss.
Some of our codebase is using the date-fns violating their types 

For example, `toDate` has the following implementation inside date-fns library
```js
function toDate(argument) {
  const argStr = Object.prototype.toString.call(argument);

  // Clone the date
  if (
    argument instanceof Date ||
    (typeof argument === "object" && argStr === "[object Date]")
  ) {
    // Prevent the date to lose the milliseconds when passed to new Date() in IE10
    return new argument.constructor(+argument);
  } else if (
    typeof argument === "number" ||
    argStr === "[object Number]" ||
    typeof argument === "string" ||
    argStr === "[object String]"
  ) {
    // TODO: Can we get rid of as?
    return new Date(argument);
  } else {
    // TODO: Can we get rid of as?
    return new Date(NaN);
  }
}
```

and as you can see, `else` conditions are under question because here is the type counterpart 
```ts
export declare function toDate<DateType extends Date>(
  argument: DateType | number | string,
): DateType;
```
The function shouldn't accept `null` or `undefined`, and our Date constructor `newDate` can return `null`, and then this null is used in several places violating the `toDate`'s type contract.


So, should I suppress ts errors and leave everything as is it will save the status quo, but in the future may cause problems related to date-fns if they remove undocumented pieces of code 

OR 

I'll add some default values for such cases, and we won't violate ts contracts, but it may break someones business logic and we should release this under a major version 


## Contribution checklist
- [x] I have followed the [contributing guidelines](https://github.com/Hacker0x01/react-datepicker/blob/main/CONTRIBUTING.md).
- [x] I have added sufficient test coverage for my changes.
- [x] I have formatted my code with Prettier and checked for linting issues with ESLint for code readability.



Patch:
diff --git a/jest.config.js b/jest.config.js
index 93cada21..ac85d0fc 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -11,7 +11,8 @@ module.exports = {
   transformIgnorePatterns: ["/node_modules/(?!date-fns)"],
   transform: {
     "^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
-    "^.+\\.ts?$": "ts-jest",
+    // TODO: use it after the migration
+    // "^.+\\.ts?$": "ts-jest",
     "node_modules/(?!date-fns/.*)": "ts-jest",
   },
 };
diff --git a/package.json b/package.json
index 91b930e1..2a5722a0 100644
--- a/package.json
+++ b/package.json
@@ -96,8 +96,8 @@
     "precommit": "lint-staged --allow-empty",
     "sass-lint": "stylelint 'src/stylesheets/*.scss'",
     "lint": "yarn run eslint && yarn run sass-lint",
-    "prettier": "prettier --write '**/*.{js,jsx}'",
-    "prettier:check": "prettier --check '**/*.{js,jsx}'",
+    "prettier": "prettier --write '**/*.{js,jsx,ts,tsx}'",
+    "prettier:check": "prettier --check '**/*.{js,jsx,ts,tsx}'",
     "start": "yarn --cwd docs-site install && yarn --cwd docs-site start",
     "test": "NODE_ENV=test jest",
     "test:ci": "NODE_ENV=test jest --ci --coverage",
diff --git a/src/date_utils.js b/src/date_utils.ts
similarity index 50%
rename from src/date_utils.js
rename to src/date_utils.ts
index 4e58002b..c22483bb 100644
--- a/src/date_utils.js
+++ b/src/date_utils.ts
@@ -55,7 +55,22 @@ import { isWithinInterval } from "date-fns/isWithinInterval";
 import { toDate } from "date-fns/toDate";
 import { parse } from "date-fns/parse";
 import { parseISO } from "date-fns/parseISO";
-import { addSeconds } from "date-fns";
+import { type Locale, addSeconds, Day } from "date-fns";
+
+interface LocaleObj
+  extends Pick<Locale, "options" | "formatLong" | "localize" | "match"> {}
+
+function getLocaleScope() {
+  // Use this cast to avoid messing with users globalThis (like window) and the rest of keys in the globalThis object we don't care about
+  const scope = (typeof window !== "undefined"
+    ? window
+    : globalThis) as unknown as {
+    __localeId__: string;
+    __localeData__: Record<string, LocaleObj>;
+  };
+
+  return scope;
+}
 
 export const DEFAULT_YEAR_ITEM_NUMBER = 12;
 
@@ -65,16 +80,32 @@ const longFormattingTokensRegExp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g;
 
 // ** Date Constructors **
 
-export function newDate(value) {
-  const d = value
-    ? typeof value === "string" || value instanceof String
-      ? parseISO(value)
-      : toDate(value)
-    : new Date();
-  return isValid(d) ? d : null;
+export function newDate(value?: string | Date | number | null): Date {
+  if (value == null) {
+    return new Date();
+  }
+
+  const d = typeof value === "string" ? parseISO(value) : toDate(value);
+  return isValid(d) ? d : new Date();
 }
 
-export function parseDate(value, dateFormat, locale, strictParsing, minDate) {
+/**
+ * Parses a date.
+ *
+ * @param value - The string representing the Date in a parsable form, e.g., ISO 1861
+ * @param dateFormat - The date format.
+ * @param locale - The locale.
+ * @param strictParsing - The strict parsing flag.
+ * @param minDate - The minimum date.
+ * @returns - The parsed date or null.
+ */
+export function parseDate(
+  value: string,
+  dateFormat: string | string[],
+  locale: string | LocaleObj,
+  strictParsing: boolean,
+  minDate: Date,
+): Date | null {
   let parsedDate = null;
   let localeObject =
     getLocaleObject(locale) || getLocaleObject(getDefaultLocale());
@@ -109,12 +140,12 @@ export function parseDate(value, dateFormat, locale, strictParsing, minDate) {
       isValid(parsedDate) &&
       value === formatDate(parsedDate, dateFormat, locale);
   } else if (!isValid(parsedDate)) {
-    dateFormat = dateFormat
-      .match(longFormattingTokensRegExp)
+    dateFormat = (dateFormat.match(longFormattingTokensRegExp) ?? [])
       .map(function (substring) {
         const firstCharacter = substring[0];
         if (firstCharacter === "p" || firstCharacter === "P") {
-          const longFormatter = longFormatters[firstCharacter];
+          // The type in date-fns is `Record<string, LongFormatter>` so we can do our firstCharacter a bit loos but I don't think that this is a good idea
+          const longFormatter = longFormatters[firstCharacter]!;
           return localeObject
             ? longFormatter(substring, localeObject.formatLong)
             : firstCharacter;
@@ -142,21 +173,43 @@ export function parseDate(value, dateFormat, locale, strictParsing, minDate) {
 
 export { isDate };
 
-export function isValid(date, minDate) {
-  minDate = minDate ? minDate : new Date("1/1/1000");
+/**
+ * Checks if a given date is valid and not before the minimum date.
+ * @param date - The date to be checked.
+ * @param minDate - The minimum date allowed. If not provided, defaults to "1/1/1800".
+ * @returns A boolean value indicating whether the date is valid and not before the minimum date.
+ */
+export function isValid(date: Date, minDate?: Date): boolean {
+  /* the fallback date is essential to not break test case
+   * `should auto update calendar when the updated date text is after props.minDate`
+   * and backward compatibility respectfully
+   */
+  minDate = minDate ?? new Date("1/1/1800");
   return isValidDate(date) && !isBefore(date, minDate);
 }
 
 // ** Date Formatting **
 
-export function formatDate(date, formatStr, locale) {
+/**
+ * Formats a date.
+ *
+ * @param date - The date.
+ * @param formatStr - The format string.
+ * @param locale - The locale.
+ * @returns - The formatted date.
+ */
+export function formatDate(
+  date: Date,
+  formatStr: string,
+  locale?: string | LocaleObj,
+): string {
   if (locale === "en") {
     return format(date, formatStr, {
       useAdditionalWeekYearTokens: true,
       useAdditionalDayOfYearTokens: true,
     });
   }
-  let localeObj = getLocaleObject(locale);
+  let localeObj = locale ? getLocaleObject(locale) : undefined;
   if (locale && !localeObj) {
     console.warn(
       `A locale object was not found for the provided string ["${locale}"].`,
@@ -170,25 +223,47 @@ export function formatDate(date, formatStr, locale) {
     localeObj = getLocaleObject(getDefaultLocale());
   }
   return format(date, formatStr, {
-    locale: localeObj ? localeObj : null,
+    locale: localeObj,
     useAdditionalWeekYearTokens: true,
     useAdditionalDayOfYearTokens: true,
   });
 }
 
-export function safeDateFormat(date, { dateFormat, locale }) {
-  return (
-    (date &&
-      formatDate(
-        date,
-        Array.isArray(dateFormat) ? dateFormat[0] : dateFormat,
-        locale,
-      )) ||
-    ""
-  );
+/**
+ * Safely formats a date.
+ *
+ * @param date - The date.
+ * @param options - An object containing the dateFormat and locale.
+ * @returns - The formatted date or an empty string.
+ */
+export function safeDateFormat(
+  date: Date,
+  {
+    dateFormat,
+    locale,
+  }: { dateFormat: string | string[]; locale: string | LocaleObj },
+): string {
+  const formatStr = (
+    Array.isArray(dateFormat) && dateFormat.length > 0
+      ? dateFormat[0]
+      : dateFormat
+  ) as string; // Cast to string because it's impossible to get `string | string[] | undefined` here and typescript doesn't know that
+  return (date && formatDate(date, formatStr, locale)) || "";
 }
 
-export function safeDateRangeFormat(startDate, endDate, props) {
+/**
+ * Safely formats a date range.
+ *
+ * @param startDate - The start date.
+ * @param endDate - The end date.
+ * @param props - The props.
+ * @returns - The formatted date range or an empty string.
+ */
+export function safeDateRangeFormat(
+  startDate: Date,
+  endDate: Date,
+  props: { dateFormat: string | string[]; locale: string | LocaleObj },
+): string {
   if (!startDate) {
     return "";
   }
@@ -199,15 +274,27 @@ export function safeDateRangeFormat(startDate, endDate, props) {
   return `${formattedStartDate} - ${formattedEndDate}`;
 }
 
-export function safeMultipleDatesFormat(dates, props) {
+/**
+ * Safely formats multiple dates.
+ *
+ * @param dates - The dates.
+ * @param props - The props.
+ * @returns - The formatted dates or an empty string.
+ */
+export function safeMultipleDatesFormat(
+  dates: Date[],
+  props: { dateFormat: string | string[]; locale: string | LocaleObj },
+): string {
   if (!dates?.length) {
     return "";
   }
-  const formattedFirstDate = safeDateFormat(dates[0], props);
+
+  let formattedFirstDate = dates[0] ? safeDateFormat(dates[0], props) : "";
   if (dates.length === 1) {
     return formattedFirstDate;
   }
-  if (dates.length === 2) {
+
+  if (dates.length === 2 && dates[1]) {
     const formattedSecondDate = safeDateFormat(dates[1], props);
     return `${formattedFirstDate}, ${formattedSecondDate}`;
   }
@@ -215,10 +302,19 @@ export function safeMultipleDatesFormat(dates, props) {
   const extraDatesCount = dates.length - 1;
   return `${formattedFirstDate} (+${extraDatesCount})`;
 }
-
 // ** Date Setters **
 
-export function setTime(date, { hour = 0, minute = 0, second = 0 }) {
+/**
+ * Sets the time for a given date.
+ *
+ * @param date - The date.
+ * @param time - An object containing the hour, minute, and second.
+ * @returns - The date with the time set.
+ */
+export function setTime(
+  date: Date,
+  { hour = 0, minute = 0, second = 0 },
+): Date {
   return setHours(setMinutes(setSeconds(date, second), minute), hour);
 }
 
@@ -239,24 +335,55 @@ export {
   getTime,
 };
 
-export function getWeek(date, locale) {
-  let localeObj =
-    (locale && getLocaleObject(locale)) ||
-    (getDefaultLocale() && getLocaleObject(getDefaultLocale()));
-  return getISOWeek(date, localeObj ? { locale: localeObj } : null);
+/**
+ * Gets the week of the year for a given date.
+ *
+ * @param date - The date.
+ * @returns - The week of the year.
+ */
+export function getWeek(date: Date): number {
+  return getISOWeek(date);
 }
 
-export function getDayOfWeekCode(day, locale) {
+/**
+ * Gets the day of the week code for a given day.
+ *
+ * @param day - The day.
+ * @param locale - The locale.
+ * @returns - The day of the week code.
+ */
+export function getDayOfWeekCode(
+  day: Date,
+  locale: string | LocaleObj,
+): string {
   return formatDate(day, "ddd", locale);
 }
 
 // *** Start of ***
 
-export function getStartOfDay(date) {
+/**
+ * Gets the start of the day for a given date.
+ *
+ * @param date - The date.
+ * @returns - The start of the day.
+ */
+export function getStartOfDay(date: Date): Date {
   return startOfDay(date);
 }
 
-export function getStartOfWeek(date, locale, calendarStartDay) {
+/**
+ * Gets the start of the week for a given date.
+ *
+ * @param date - The date.
+ * @param locale - The locale.
+ * @param calendarStartDay - The day the calendar starts on.
+ * @returns - The start of the week.
+ */
+export function getStartOfWeek(
+  date: Date,
+  locale: string | LocaleObj,
+  calendarStartDay: Day,
+): Date {
   let localeObj = locale
     ? getLocaleObject(locale)
     : getLocaleObject(getDefaultLocale());
@@ -266,29 +393,63 @@ export function getStartOfWeek(date, locale, calendarStartDay) {
   });
 }
 
-export function getStartOfMonth(date) {
+/**
+ * Gets the start of the month for a given date.
+ *
+ * @param date - The date.
+ * @returns - The start of the month.
+ */
+export function getStartOfMonth(date: Date): Date {
   return startOfMonth(date);
 }
 
-export function getStartOfYear(date) {
+/**
+ * Gets the start of the year for a given date.
+ *
+ * @param date - The date.
+ * @returns - The start of the year.
+ */
+export function getStartOfYear(date: Date): Date {
   return startOfYear(date);
 }
 
-export function getStartOfQuarter(date) {
+/**
+ * Gets the start of the quarter for a given date.
+ *
+ * @param date - The date.
+ * @returns - The start of the quarter.
+ */
+export function getStartOfQuarter(date: Date): Date {
   return startOfQuarter(date);
 }
 
-export function getStartOfToday() {
+/**
+ * Gets the start of today.
+ *
+ * @returns - The start of today.
+ */
+export function getStartOfToday(): Date {
   return startOfDay(newDate());
 }
 
 // *** End of ***
-
-export function getEndOfWeek(date) {
+/**
+ * Gets the end of the week for a given date.
+ *
+ * @param date - The date.
+ * @returns - The end of the week.
+ */
+export function getEndOfWeek(date: Date): Date {
   return endOfWeek(date);
 }
 
-export function getEndOfMonth(date) {
+/**
+ * Gets the end of the month for a given date.
+ *
+ * @param date - The date.
+ * @returns - The end of the month.
+ */
+export function getEndOfMonth(date: Date): Date {
   return endOfMonth(date);
 }
 
@@ -314,7 +475,14 @@ export { addHours, subDays, subWeeks, subMonths, subQuarters, subYears };
 
 export { isBefore, isAfter };
 
-export function isSameYear(date1, date2) {
+/**
+ * Checks if two dates are in the same year.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - True if the dates are in the same year, false otherwise.
+ */
+export function isSameYear(date1: Date | null, date2: Date | null): boolean {
   if (date1 && date2) {
     return dfIsSameYear(date1, date2);
   } else {
@@ -322,7 +490,14 @@ export function isSameYear(date1, date2) {
   }
 }
 
-export function isSameMonth(date1, date2) {
+/**
+ * Checks if two dates are in the same month.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - True if the dates are in the same month, false otherwise.
+ */
+export function isSameMonth(date1: Date | null, date2: Date | null): boolean {
   if (date1 && date2) {
     return dfIsSameMonth(date1, date2);
   } else {
@@ -330,7 +505,14 @@ export function isSameMonth(date1, date2) {
   }
 }
 
-export function isSameQuarter(date1, date2) {
+/**
+ * Checks if two dates are in the same quarter.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - True if the dates are in the same quarter, false otherwise.
+ */
+export function isSameQuarter(date1: Date | null, date2: Date | null): boolean {
   if (date1 && date2) {
     return dfIsSameQuarter(date1, date2);
   } else {
@@ -338,7 +520,14 @@ export function isSameQuarter(date1, date2) {
   }
 }
 
-export function isSameDay(date1, date2) {
+/**
+ * Checks if two dates are on the same day.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - True if the dates are on the same day, false otherwise.
+ */
+export function isSameDay(date1: Date | null, date2: Date | null): boolean {
   if (date1 && date2) {
     return dfIsSameDay(date1, date2);
   } else {
@@ -346,7 +535,14 @@ export function isSameDay(date1, date2) {
   }
 }
 
-export function isEqual(date1, date2) {
+/**
+ * Checks if two dates are equal.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - True if the dates are equal, false otherwise.
+ */
+export function isEqual(date1: Date | null, date2: Date | null): boolean {
   if (date1 && date2) {
     return dfIsEqual(date1, date2);
   } else {
@@ -354,7 +550,19 @@ export function isEqual(date1, date2) {
   }
 }
 
-export function isDayInRange(day, startDate, endDate) {
+/**
+ * Checks if a day is within a date range.
+ *
+ * @param day - The day to check.
+ * @param startDate - The start date of the range.
+ * @param endDate - The end date of the range.
+ * @returns - True if the day is within the range, false otherwise.
+ */
+export function isDayInRange(
+  day: Date,
+  startDate: Date,
+  endDate: Date,
+): boolean {
   let valid;
   const start = startOfDay(startDate);
   const end = endOfDay(endDate);
@@ -369,14 +577,31 @@ export function isDayInRange(day, startDate, endDate) {
 
 // *** Diffing ***
 
-export function getDaysDiff(date1, date2) {
+/**
+ * Gets the difference in days between two dates.
+ *
+ * @param date1 - The first date.
+ * @param date2 - The second date.
+ * @returns - The difference in days.
+ */
+export function getDaysDiff(date1: Date, date2: Date): number {
   return differenceInCalendarDays(date1, date2);
 }
 
 // ** Date Localization **
 
-export function registerLocale(localeName, localeData) {
-  const scope = typeof window !== "undefined" ? window : globalThis;
+/**
+ * Registers a locale.
+ *
+ * @param localeName - The name of the locale.
+ * @param localeData - The data of the locale.
+ */
+
+export function registerLocale(
+  localeName: string,
+  localeData: LocaleObj,
+): void {
+  const scope = getLocaleScope();
 
   if (!scope.__localeData__) {
     scope.__localeData__ = {};
@@ -384,57 +609,143 @@ export function registerLocale(localeName, localeData) {
   scope.__localeData__[localeName] = localeData;
 }
 
-export function setDefaultLocale(localeName) {
-  const scope = typeof window !== "undefined" ? window : globalThis;
+/**
+ * Sets the default locale.
+ *
+ * @param localeName - The name of the locale.
+ */
+export function setDefaultLocale(localeName: string): void {
+  const scope = getLocaleScope();
 
   scope.__localeId__ = localeName;
 }
 
-export function getDefaultLocale() {
-  const scope = typeof window !== "undefined" ? window : globalThis;
+/**
+ * Gets the default locale.
+ *
+ * @returns - The default locale.
+ */
+export function getDefaultLocale(): string {
+  const scope = getLocaleScope();
 
   return scope.__localeId__;
 }
 
-export function getLocaleObject(localeSpec) {
+/**
+ * Gets the locale object.
+ *
+ * @param localeSpec - The locale specification.
+ * @returns - The locale object.
+ */
+export function getLocaleObject(
+  localeSpec: string | LocaleObj,
+): LocaleObj | undefined {
   if (typeof localeSpec === "string") {
     // Treat it as a locale name registered by registerLocale
-    const scope = typeof window !== "undefined" ? window : globalThis;
-    return scope.__localeData__ ? scope.__localeData__[localeSpec] : null;
+    const scope = getLocaleScope();
+    // Null was replaced with undefined to avoid type coercion
+    return scope.__localeData__ ? scope.__localeData__[localeSpec] : undefined;
   } else {
     // Treat it as a raw date-fns locale object
     return localeSpec;
   }
 }
 
-export function getFormattedWeekdayInLocale(date, formatFunc, locale) {
+/**
+ * Formats the weekday in a given locale.
+ *
+ * @param date - The date to format.
+ * @param formatFunc - The formatting function.
+ * @param locale - The locale to use for formatting.
+ * @returns - The formatted weekday.
+ */
+export function getFormattedWeekdayInLocale(
+  date: Date,
+  formatFunc: (date: string) => string,
+  locale: string,
+): string {
   return formatFunc(formatDate(date, "EEEE", locale));
 }
 
-export function getWeekdayMinInLocale(date, locale) {
+/**
+ * Gets the minimum weekday in a given locale.
+ *
+ * @param date - The date to format.
+ * @param locale - The locale to use for formatting.
+ * @returns - The minimum weekday.
+ */
+export function getWeekdayMinInLocale(date: Date, locale: string): string {
   return formatDate(date, "EEEEEE", locale);
 }
 
-export function getWeekdayShortInLocale(date, locale) {
+/**
+ * Gets the short weekday in a given locale.
+ *
+ * @param date - The date to format.
+ * @param locale - The locale to use for formatting.
+ * @returns - The short weekday.
+ */
+export function getWeekdayShortInLocale(date: Date, locale: string): string {
   return formatDate(date, "EEE", locale);
 }
 
-export function getMonthInLocale(month, locale) {
+/**
+ * Gets the month in a given locale.
+ *
+ * @param month - The month to format.
+ * @param locale - The locale to use for formatting.
+ * @returns - The month.
+ */
+export function getMonthInLocale(month: number, locale: string): string {
   return formatDate(setMonth(newDate(), month), "LLLL", locale);
 }
 
-export function getMonthShortInLocale(month, locale) {
+/**
+ * Gets the short month in a given locale.
+ *
+ * @param month - The month to format.
+ * @param locale - The locale to use for formatting.
+ * @returns - The short month.
+ */
+export function getMonthShortInLocale(month: number, locale: string): string {
   return formatDate(setMonth(newDate(), month), "LLL", locale);
 }
 
-export function getQuarterShortInLocale(quarter, locale) {
+/**
+ * Gets the short quarter in a given locale.
+ *
+ * @param quarter - The quarter to format.
+ * @param locale - The locale to use for formatting.
+ * @returns - The short quarter.
+ */
+export function getQuarterShortInLocale(
+  quarter: number,
+  locale: string,
+): string {
   return formatDate(setQuarter(newDate(), quarter), "QQQ", locale);
 }
 
 // ** Utils for some components **
 
+interface DayDisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  excludeDates?: { date?: Date }[] | Date[];
+  excludeDateIntervals?: { start: Date; end: Date }[];
+  includeDates?: Date[];
+  includeDateIntervals?: { start: Date; end: Date }[];
+  filterDate?: (date: Date) => boolean;
+}
+
+/**
+ * Checks if a day is disabled.
+ *
+ * @param day - The day to check.
+ * @param options - The options to consider when checking.
+ * @returns - Returns true if the day is disabled, false otherwise.
+ */
 export function isDayDisabled(
-  day,
+  day: Date,
   {
     minDate,
     maxDate,
@@ -443,14 +754,18 @@ export function isDayDisabled(
     includeDates,
     includeDateIntervals,
     filterDate,
-  } = {},
-) {
+  }: DayDisabledOptions = {},
+): boolean {
   return (
     isOutOfBounds(day, { minDate, maxDate }) ||
     (excludeDates &&
-      excludeDates.some((excludeDate) =>
-        isSameDay(day, excludeDate.date ? excludeDate.date : excludeDate),
-      )) ||
+      excludeDates.some((excludeDate) => {
+        if (excludeDate instanceof Date) {
+          return isSameDay(day, excludeDate);
+        } else {
+          return isSameDay(day, excludeDate.date ?? new Date());
+        }
+      })) ||
     (excludeDateIntervals &&
       excludeDateIntervals.some(({ start, end }) =>
         isWithinInterval(day, { start, end }),
@@ -466,10 +781,22 @@ export function isDayDisabled(
   );
 }
 
+interface DayExcludedOptions {
+  excludeDates?: { date?: Date }[] | Date[];
+  excludeDateIntervals?: { start: Date; end: Date }[];
+}
+
+/**
+ * Checks if a day is excluded.
+ *
+ * @param day - The day to check.
+ * @param options - The options to consider when checking.
+ * @returns - Returns true if the day is excluded, false otherwise.
+ */
 export function isDayExcluded(
-  day,
-  { excludeDates, excludeDateIntervals } = {},
-) {
+  day: Date,
+  { excludeDates, excludeDateIntervals }: DayExcludedOptions = {},
+): boolean {
   if (excludeDateIntervals && excludeDateIntervals.length > 0) {
     return excludeDateIntervals.some(({ start, end }) =>
       isWithinInterval(day, { start, end }),
@@ -477,21 +804,39 @@ export function isDayExcluded(
   }
   return (
     (excludeDates &&
-      excludeDates.some((excludeDate) =>
-        isSameDay(day, excludeDate.date ? excludeDate.date : excludeDate),
-      )) ||
+      excludeDates.some((excludeDate) => {
+        if (excludeDate instanceof Date) {
+          return isSameDay(day, excludeDate);
+        } else {
+          return isSameDay(day, excludeDate.date ?? new Date());
+        }
+      })) ||
     false
   );
 }
 
+interface MonthDisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  excludeDates?: Date[];
+  includeDates?: Date[];
+  filterDate?: (date: Date) => boolean;
+}
+
 export function isMonthDisabled(
-  month,
-  { minDate, maxDate, excludeDates, includeDates, filterDate } = {},
-) {
+  month: Date,
+  {
+    minDate,
+    maxDate,
+    excludeDates,
+    includeDates,
+    filterDate,
+  }: MonthDisabledOptions = {},
+): boolean {
   return (
     isOutOfBounds(month, {
-      minDate: startOfMonth(minDate),
-      maxDate: endOfMonth(maxDate),
+      minDate: minDate ? startOfMonth(minDate) : undefined,
+      maxDate: maxDate ? endOfMonth(maxDate) : undefined,
     }) ||
     (excludeDates &&
       excludeDates.some((excludeDate) => isSameMonth(month, excludeDate))) ||
@@ -502,7 +847,12 @@ export function isMonthDisabled(
   );
 }
 
-export function isMonthInRange(startDate, endDate, m, day) {
+export function isMonthInRange(
+  startDate: Date,
+  endDate: Date,
+  m: number,
+  day: Date,
+): boolean {
   const startDateYear = getYear(startDate);
   const startDateMonth = getMonth(startDate);
   const endDateYear = getYear(endDate);
@@ -517,12 +867,27 @@ export function isMonthInRange(startDate, endDate, m, day) {
       (dayYear < endDateYear && dayYear > startDateYear)
     );
   }
+  return false;
+}
+
+interface QuarterDisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  excludeDates?: Date[];
+  includeDates?: Date[];
+  filterDate?: (date: Date) => boolean;
 }
 
 export function isQuarterDisabled(
-  quarter,
-  { minDate, maxDate, excludeDates, includeDates, filterDate } = {},
-) {
+  quarter: Date,
+  {
+    minDate,
+    maxDate,
+    excludeDates,
+    includeDates,
+    filterDate,
+  }: QuarterDisabledOptions = {},
+): boolean {
   return (
     isOutOfBounds(quarter, { minDate, maxDate }) ||
     (excludeDates &&
@@ -538,13 +903,7 @@ export function isQuarterDisabled(
   );
 }
 
-/**
- * @param {number} year
- * @param {Date} start
- * @param {Date} end
- * @returns {boolean}
- */
-export function isYearInRange(year, start, end) {
+export function isYearInRange(year: number, start: Date, end: Date): boolean {
   if (!isValidDate(start) || !isValidDate(end)) return false;
   const startYear = getYear(start);
   const endYear = getYear(end);
@@ -552,15 +911,29 @@ export function isYearInRange(year, start, end) {
   return startYear <= year && endYear >= year;
 }
 
+interface YearDisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  excludeDates?: Date[];
+  includeDates?: Date[];
+  filterDate?: (date: Date) => boolean;
+}
+
 export function isYearDisabled(
-  year,
-  { minDate, maxDate, excludeDates, includeDates, filterDate } = {},
-) {
+  year: number,
+  {
+    minDate,
+    maxDate,
+    excludeDates,
+    includeDates,
+    filterDate,
+  }: YearDisabledOptions = {},
+): boolean {
   const date = new Date(year, 0, 1);
   return (
     isOutOfBounds(date, {
-      minDate: startOfYear(minDate),
-      maxDate: endOfYear(maxDate),
+      minDate: minDate ? startOfYear(minDate) : undefined,
+      maxDate: maxDate ? endOfYear(maxDate) : undefined,
     }) ||
     (excludeDates &&
       excludeDates.some((excludeDate) => isSameYear(date, excludeDate))) ||
@@ -571,7 +944,12 @@ export function isYearDisabled(
   );
 }
 
-export function isQuarterInRange(startDate, endDate, q, day) {
+export function isQuarterInRange(
+  startDate: Date,
+  endDate: Date,
+  q: number,
+  day: Date,
+): boolean {
   const startDateYear = getYear(startDate);
   const startDateQuarter = getQuarter(startDate);
   const endDateYear = getYear(endDate);
@@ -586,16 +964,26 @@ export function isQuarterInRange(startDate, endDate, q, day) {
       (dayYear < endDateYear && dayYear > startDateYear)
     );
   }
+  return false;
+}
+
+interface BoundsOptions {
+  minDate?: Date;
+  maxDate?: Date;
 }
 
-export function isOutOfBounds(day, { minDate, maxDate } = {}) {
+export function isOutOfBounds(
+  day: Date,
+  { minDate, maxDate }: BoundsOptions = {},
+): boolean {
   return (
-    (minDate && differenceInCalendarDays(day, minDate) < 0) ||
-    (maxDate && differenceInCalendarDays(day, maxDate) > 0)
+    ((minDate && differenceInCalendarDays(day, minDate) < 0) ||
+      (maxDate && differenceInCalendarDays(day, maxDate) > 0)) ??
+    false
   );
 }
 
-export function isTimeInList(time, times) {
+export function isTimeInList(time: Date, times: Date[]): boolean {
   return times.some(
     (listTime) =>
       getHours(listTime) === getHours(time) &&
@@ -604,10 +992,16 @@ export function isTimeInList(time, times) {
   );
 }
 
+interface TimeDisabledOptions {
+  excludeTimes?: Date[];
+  includeTimes?: Date[];
+  filterTime?: (time: Date) => boolean;
+}
+
 export function isTimeDisabled(
-  time,
-  { excludeTimes, includeTimes, filterTime } = {},
-) {
+  time: Date,
+  { excludeTimes, includeTimes, filterTime }: TimeDisabledOptions = {},
+): boolean {
   return (
     (excludeTimes && isTimeInList(time, excludeTimes)) ||
     (includeTimes && !isTimeInList(time, includeTimes)) ||
@@ -616,7 +1010,15 @@ export function isTimeDisabled(
   );
 }
 
-export function isTimeInDisabledRange(time, { minTime, maxTime }) {
+interface TimeDisabledOptions {
+  minTime?: Date;
+  maxTime?: Date;
+}
+
+export function isTimeInDisabledRange(
+  time: Date,
+  { minTime, maxTime }: TimeDisabledOptions,
+): boolean {
   if (!minTime || !maxTime) {
     throw new Error("Both minTime and maxTime props required");
   }
@@ -644,7 +1046,16 @@ export function isTimeInDisabledRange(time, { minTime, maxTime }) {
   return valid;
 }
 
-export function monthDisabledBefore(day, { minDate, includeDates } = {}) {
+interface DisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  includeDates?: Date[];
+}
+
+export function monthDisabledBefore(
+  day: Date,
+  { minDate, includeDates }: DisabledOptions = {},
+): boolean {
   const previousMonth = subMonths(day, 1);
   return (
     (minDate && differenceInCalendarMonths(minDate, previousMonth) > 0) ||
@@ -657,7 +1068,10 @@ export function monthDisabledBefore(day, { minDate, includeDates } = {}) {
   );
 }
 
-export function monthDisabledAfter(day, { maxDate, includeDates } = {}) {
+export function monthDisabledAfter(
+  day: Date,
+  { maxDate, includeDates }: DisabledOptions = {},
+): boolean {
   const nextMonth = addMonths(day, 1);
   return (
     (maxDate && differenceInCalendarMonths(nextMonth, maxDate) > 0) ||
@@ -669,7 +1083,10 @@ export function monthDisabledAfter(day, { maxDate, includeDates } = {}) {
   );
 }
 
-export function quarterDisabledBefore(date, { minDate, includeDates } = {}) {
+export function quarterDisabledBefore(
+  date: Date,
+  { minDate, includeDates }: DisabledOptions = {},
+): boolean {
   const firstDateOfYear = startOfYear(date);
   const previousQuarter = subQuarters(firstDateOfYear, 1);
 
@@ -684,7 +1101,10 @@ export function quarterDisabledBefore(date, { minDate, includeDates } = {}) {
   );
 }
 
-export function quarterDisabledAfter(date, { maxDate, includeDates } = {}) {
+export function quarterDisabledAfter(
+  date: Date,
+  { maxDate, includeDates }: DisabledOptions = {},
+): boolean {
   const lastDateOfYear = endOfYear(date);
   const nextQuarter = addQuarters(lastDateOfYear, 1);
 
@@ -699,7 +1119,17 @@ export function quarterDisabledAfter(date, { maxDate, includeDates } = {}) {
   );
 }
 
-export function yearDisabledBefore(day, { minDate, includeDates } = {}) {
+interface YearDisabledOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  includeDates?: Date[];
+  yearItemNumber?: number;
+}
+
+export function yearDisabledBefore(
+  day: Date,
+  { minDate, includeDates }: YearDisabledOptions = {},
+): boolean {
   const previousYear = subYears(day, 1);
   return (
     (minDate && differenceInCalendarYears(minDate, previousYear) > 0) ||
@@ -713,16 +1143,22 @@ export function yearDisabledBefore(day, { minDate, includeDates } = {}) {
 }
 
 export function yearsDisabledBefore(
-  day,
-  { minDate, yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER } = {},
-) {
+  day: Date,
+  {
+    minDate,
+    yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER,
+  }: YearDisabledOptions = {},
+): boolean {
   const previousYear = getStartOfYear(subYears(day, yearItemNumber));
   const { endPeriod } = getYearsPeriod(previousYear, yearItemNumber);
   const minDateYear = minDate && getYear(minDate);
   return (minDateYear && minDateYear > endPeriod) || false;
 }
 
-export function yearDisabledAfter(day, { maxDate, includeDates } = {}) {
+export function yearDisabledAfter(
+  day: Date,
+  { maxDate, includeDates }: YearDisabledOptions = {},
+): boolean {
   const nextYear = addYears(day, 1);
   return (
     (maxDate && differenceInCalendarYears(nextYear, maxDate) > 0) ||
@@ -734,17 +1170,34 @@ export function yearDisabledAfter(day, { maxDate, includeDates } = {}) {
   );
 }
 
+interface YearsDisabledAfterOptions {
+  maxDate?: Date;
+  yearItemNumber?: number;
+}
+
 export function yearsDisabledAfter(
-  day,
-  { maxDate, yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER } = {},
-) {
+  day: Date,
+  {
+    maxDate,
+    yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER,
+  }: YearsDisabledAfterOptions = {},
+): boolean {
   const nextYear = addYears(day, yearItemNumber);
   const { startPeriod } = getYearsPeriod(nextYear, yearItemNumber);
   const maxDateYear = maxDate && getYear(maxDate);
   return (maxDateYear && maxDateYear < startPeriod) || false;
 }
 
-export function getEffectiveMinDate({ minDate, includeDates }) {
+interface EffectiveDateOptions {
+  minDate?: Date;
+  maxDate?: Date;
+  includeDates?: Date[];
+}
+
+export function getEffectiveMinDate({
+  minDate,
+  includeDates,
+}: EffectiveDateOptions): Date | undefined {
   if (includeDates && minDate) {
     let minDates = includeDates.filter(
       (includeDate) => differenceInCalendarDays(includeDate, minDate) >= 0,
@@ -757,7 +1210,10 @@ export function getEffectiveMinDate({ minDate, includeDates }) {
   }
 }
 
-export function getEffectiveMaxDate({ maxDate, includeDates }) {
+export function getEffectiveMaxDate({
+  maxDate,
+  includeDates,
+}: EffectiveDateOptions): Date | undefined {
   if (includeDates && maxDate) {
     let maxDates = includeDates.filter(
       (includeDate) => differenceInCalendarDays(includeDate, maxDate) <= 0,
@@ -770,11 +1226,21 @@ export function getEffectiveMaxDate({ maxDate, includeDates }) {
   }
 }
 
+interface HighlightDate {
+  [className: string]: Date[];
+}
+
+/**
+ * Get a map of highlighted dates with their corresponding classes.
+ * @param highlightDates The dates to highlight.
+ * @param defaultClassName The default class to use for highlighting.
+ * @returns A map with dates as keys and arrays of class names as values.
+ */
 export function getHightLightDaysMap(
-  highlightDates = [],
-  defaultClassName = "react-datepicker__day--highlighted",
-) {
-  const dateClasses = new Map();
+  highlightDates: (Date | HighlightDate)[] = [],
+  defaultClassName: string = "react-datepicker__day--highlighted",
+): Map<string, string[]> {
+  const dateClasses = new Map<string, string[]>();
   for (let i = 0, len = highlightDates.length; i < len; i++) {
     const obj = highlightDates[i];
     if (isDate(obj)) {
@@ -786,15 +1252,18 @@ export function getHightLightDaysMap(
       }
     } else if (typeof obj === "object") {
       const keys = Object.keys(obj);
-      const className = keys[0];
-      const arrOfDates = obj[keys[0]];
-      if (typeof className === "string" && arrOfDates.constructor === Array) {
+      const className = keys[0] ?? "";
+      const arrOfDates = obj[className];
+      if (typeof className === "string" && Array.isArray(arrOfDates)) {
         for (let k = 0, len = arrOfDates.length; k < len; k++) {
-          const key = formatDate(arrOfDates[k], "MM.dd.yyyy");
-          const classNamesArr = dateClasses.get(key) || [];
-          if (!classNamesArr.includes(className)) {
-            classNamesArr.push(className);
-            dateClasses.set(key, classNamesArr);
+          const dateK = arrOfDates[k];
+          if (dateK) {
+            const key = formatDate(dateK, "MM.dd.yyyy");
+            const classNamesArr = dateClasses.get(key) || [];
+            if (!classNamesArr.includes(className)) {
+              classNamesArr.push(className);
+              dateClasses.set(key, classNamesArr);
+            }
           }
         }
       }
@@ -805,11 +1274,11 @@ export function getHightLightDaysMap(
 
 /**
  * Compare the two arrays
- * @param {Array} array1
- * @param {Array} array2
- * @returns {Boolean} true, if the passed array are equal, false otherwise
+ * @param array1 The first array to compare.
+ * @param array2 The second array to compare.
+ * @returns true, if the passed arrays are equal, false otherwise.
  */
-export function arraysAreEqual(array1, array2) {
+export function arraysAreEqual<T>(array1: T[], array2: T[]): boolean {
   if (array1.length !== array2.length) {
     return false;
   }
@@ -817,17 +1286,27 @@ export function arraysAreEqual(array1, array2) {
   return array1.every((value, index) => value === array2[index]);
 }
 
+interface Holiday {
+  date: Date;
+  holidayName: string;
+}
+
+interface ClassNamesObj {
+  className: string;
+  holidayNames: string[];
+}
+
 /**
  * Assign the custom class to each date
- * @param {Array} holidayDates array of object containing date and name of the holiday
- * @param {string} classname to be added.
- * @returns {Map} Map containing date as key and array of classname and holiday name as value
+ * @param holidayDates array of object containing date and name of the holiday
+ * @param defaultClassName className to be added.
+ * @returns Map containing date as key and array of className and holiday name as value
  */
 export function getHolidaysMap(
-  holidayDates = [],
-  defaultClassName = "react-datepicker__day--holidays",
-) {
-  const dateClasses = new Map();
+  holidayDates: Holiday[] = [],
+  defaultClassName: string = "react-datepicker__day--holidays",
+): Map<string, ClassNamesObj> {
+  const dateClasses = new Map<string, ClassNamesObj>();
   holidayDates.forEach((holiday) => {
     const { date: dateObj, holidayName } = holiday;
     if (!isDate(dateObj)) {
@@ -835,7 +1314,10 @@ export function getHolidaysMap(
     }
 
     const key = formatDate(dateObj, "MM.dd.yyyy");
-    const classNamesObj = dateClasses.get(key) || {};
+    const classNamesObj = dateClasses.get(key) || {
+      className: "",
+      holidayNames: [],
+    };
     if (
       "className" in classNamesObj &&
       classNamesObj["className"] === defaultClassName &&
@@ -854,20 +1336,32 @@ export function getHolidaysMap(
   return dateClasses;
 }
 
+/**
+ * Determines the times to inject after a given start of day, current time, and multiplier.
+ * @param startOfDay The start of the day.
+ * @param currentTime The current time.
+ * @param currentMultiplier The current multiplier.
+ * @param intervals The intervals.
+ * @param injectedTimes The times to potentially inject.
+ * @returns An array of times to inject.
+ */
 export function timesToInjectAfter(
-  startOfDay,
-  currentTime,
-  currentMultiplier,
-  intervals,
-  injectedTimes,
-) {
+  startOfDay: Date,
+  currentTime: Date,
+  currentMultiplier: number,
+  intervals: number,
+  injectedTimes: Date[],
+): Date[] {
   const l = injectedTimes.length;
-  const times = [];
+  const times: Date[] = [];
   for (let i = 0; i < l; i++) {
     let injectedTime = startOfDay;
-    injectedTime = addHours(injectedTime, getHours(injectedTimes[i]));
-    injectedTime = addMinutes(injectedTime, getMinutes(injectedTimes[i]));
-    injectedTime = addSeconds(injectedTime, getSeconds(injectedTimes[i]));
+    const injectedTimeValue = injectedTimes[i];
+    if (injectedTimeValue) {
+      injectedTime = addHours(injectedTime, getHours(injectedTimeValue));
+      injectedTime = addMinutes(injectedTime, getMinutes(injectedTimeValue));
+      injectedTime = addSeconds(injectedTime, getSeconds(injectedTimeValue));
+    }
 
     const nextTime = addMinutes(
       startOfDay,
@@ -876,29 +1370,46 @@ export function timesToInjectAfter(
 
     if (
       isAfter(injectedTime, currentTime) &&
-      isBefore(injectedTime, nextTime)
+      isBefore(injectedTime, nextTime) &&
+      injectedTimeValue != undefined
     ) {
-      times.push(injectedTimes[i]);
+      times.push(injectedTimeValue);
     }
   }
 
   return times;
 }
 
-export function addZero(i) {
+/**
+ * Adds a leading zero to a number if it's less than 10.
+ * @param i The number to add a leading zero to.
+ * @returns The number as a string, with a leading zero if it was less than 10.
+ */
+export function addZero(i: number): string {
   return i < 10 ? `0${i}` : `${i}`;
 }
 
+/**
+ * Gets the start and end years for a period.
+ * @param date The date to get the period for.
+ * @param yearItemNumber The number of years in the period. Defaults to DEFAULT_YEAR_ITEM_NUMBER.
+ * @returns An object with the start and end years for the period.
+ */
 export function getYearsPeriod(
-  date,
-  yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER,
-) {
+  date: Date,
+  yearItemNumber: number = DEFAULT_YEAR_ITEM_NUMBER,
+): { startPeriod: number; endPeriod: number } {
   const endPeriod = Math.ceil(getYear(date) / yearItemNumber) * yearItemNumber;
   const startPeriod = endPeriod - (yearItemNumber - 1);
   return { startPeriod, endPeriod };
 }
 
-export function getHoursInDay(d) {
+/**
+ * Gets the number of hours in a day.
+ * @param d The date to get the number of hours for.
+ * @returns The number of hours in the day.
+ */
+export function getHoursInDay(d: Date): number {
   const startOfDay = new Date(d.getFullYear(), d.getMonth(), d.getDate());
   const startOfTheNextDay = new Date(
     d.getFullYear(),
@@ -919,10 +1430,10 @@ export function getHoursInDay(d) {
  * See comments on https://github.com/Hacker0x01/react-datepicker/pull/4244
  * for more details
  *
- * @param {Date} d date
- * @returns {Date} start of the minute
+ * @param d date
+ * @returns start of the minute
  */
-export function startOfMinute(d) {
+export function startOfMinute(d: Date): Date {
   const seconds = d.getSeconds();
   const milliseconds = d.getMilliseconds();
 
@@ -934,22 +1445,20 @@ export function startOfMinute(d) {
  *
  * This function is a DST and timezone-safe analog of `date-fns/isSameMinute`
  *
- * @param {Date} d1
- * @param {Date} d2
- * @returns {boolean}
+ * @param d1
+ * @param d2
+ * @returns
  */
-export function isSameMinute(d1, d2) {
+export function isSameMinute(d1: Date, d2: Date): boolean {
   return startOfMinute(d1).getTime() === startOfMinute(d2).getTime();
 }
 
 /**
- * Returns a cloned date with midnight time (00:00:00)
- *
- * @param {Date} date The date for which midnight time is required
- * @param {Date} dateToCompare the date to compare with
- * @returns {Date} A new datetime object representing the input date with midnight time
+ * Returns a new datetime object representing the input date with midnight time
+ * @param date The date to get the midnight time for
+ * @returns A new datetime object representing the input date with midnight time
  */
-export function getMidnightDate(date) {
+export function getMidnightDate(date: Date): Date {
   if (!isDate(date)) {
     throw new Error("Invalid date");
   }
@@ -961,16 +1470,15 @@ export function getMidnightDate(date) {
 
 /**
  * Is the first date before the second one?
- *
- * @param {Date} date The date that should be before the other one to return true
- * @param {Date} dateToCompare The date to compare with
- * @returns {boolean} The first date is before the second date
+ * @param date The date that should be before the other one to return true
+ * @param dateToCompare The date to compare with
+ * @returns The first date is before the second date
  *
  * Note:
  *  This function considers the mid-night of the given dates for comparison.
  *  It evaluates whether date is before dateToCompare based on their mid-night timestamps.
  */
-export function isDateBefore(date, dateToCompare) {
+export function isDateBefore(date: Date, dateToCompare: Date): boolean {
   if (!isDate(date) || !isDate(dateToCompare)) {
     throw new Error("Invalid date received");
   }
@@ -981,7 +1489,13 @@ export function isDateBefore(date, dateToCompare) {
   return isBefore(midnightDate, midnightDateToCompare);
 }
 
-export function isSpaceKeyDown(event) {
+/**
+ * Checks if the space key was pressed down.
+ *
+ * @param event - The keyboard event.
+ * @returns - Returns true if the space key was pressed down, false otherwise.
+ */
+export function isSpaceKeyDown(event: KeyboardEvent): boolean {
   const SPACE_KEY = " ";
   return event.key === SPACE_KEY;
 }
diff --git a/src/input_time.tsx b/src/input_time.tsx
index dba934a8..6e890af0 100644
--- a/src/input_time.tsx
+++ b/src/input_time.tsx
@@ -42,7 +42,7 @@ export default class InputTime extends React.Component<
 
   static getDerivedStateFromProps(
     props: InputTimeProps,
-    state: InputTimeState
+    state: InputTimeState,
   ) {
     if (props.timeString !== state.time) {
       return {
diff --git a/src/tab_loop.tsx b/src/tab_loop.tsx
index f5952263..beb46657 100644
--- a/src/tab_loop.tsx
+++ b/src/tab_loop.tsx
@@ -13,7 +13,7 @@ const focusableFilter = (
     | HTMLInputElement
     | HTMLSelectElement
     | HTMLTextAreaElement
-    | HTMLAnchorElement
+    | HTMLAnchorElement,
 ) => {
   if (node instanceof HTMLAnchorElement) {
     return node.tabIndex !== -1;
@@ -67,7 +67,7 @@ export default class TabLoop extends React.Component<TabLoopProps> {
       .call(
         this.tabLoopRef.current?.querySelectorAll(focusableElementsSelector),
         1,
-        -1
+        -1,
       )
       .filter(focusableFilter);
 
diff --git a/src/with_floating.tsx b/src/with_floating.tsx
index a66240c0..8d9caa09 100644
--- a/src/with_floating.tsx
+++ b/src/with_floating.tsx
@@ -34,7 +34,7 @@ interface WithFloatingProps {
  * @returns A new component with floating behavior.
  */
 export default function withFloating<T extends {}>(
-  Component: ComponentType<T>
+  Component: ComponentType<T>,
 ) {
   const WithFloating: React.FC<T & WithFloatingProps> = (props) => {
     const alt_props = {


Test patch:
diff --git a/test/date_utils_test.test.js b/test/date_utils_test.test.js
index 9659654b..d8de1314 100644
--- a/test/date_utils_test.test.js
+++ b/test/date_utils_test.test.js
@@ -55,8 +55,10 @@ registerLocale("pt-BR", ptBR);
 
 describe("date_utils", () => {
   describe("newDate", () => {
-    it("should return null for invalid value passed", () => {
-      expect(newDate("21123asd")).toBeNull();
+    it("should return new Date() for invalid value passed", () => {
+      jest.useFakeTimers();
+      expect(newDate("21123asd")).toEqual(new Date());
+      jest.useRealTimers();
     });
   });
 
@@ -481,9 +483,11 @@ describe("date_utils", () => {
 
     it("should be enabled if after maxDate but same month", () => {
       const day = newDate("2023-01-02");
-      expect(isMonthDisabled(day, { maxDate: newDate("2023-01-01") })).toBe(
-        false,
-      );
+      expect(
+        isMonthDisabled(day, {
+          maxDate: newDate("2023-01-01"),
+        }),
+      ).toBe(false);
     });
   });
 
diff --git a/test/datepicker_test.test.js b/test/datepicker_test.test.js
index 5bf1d8af..5bba75dc 100644
--- a/test/datepicker_test.test.js
+++ b/test/datepicker_test.test.js
@@ -4,7 +4,7 @@ import { render, act, waitFor, fireEvent } from "@testing-library/react";
 import DatePicker, { registerLocale } from "../src/index.jsx";
 import TestWrapper from "./test_wrapper.jsx";
 import CustomInput from "./helper_components/custom_input.jsx";
-import * as utils from "../src/date_utils.js";
+import * as utils from "../src/date_utils.ts";
 import { getKey } from "./test_utils.js";
 
 function getSelectedDayNode(datePicker) {
diff --git a/test/month_year_dropdown_test.test.js b/test/month_year_dropdown_test.test.js
index 10153cc4..f73804f7 100644
--- a/test/month_year_dropdown_test.test.js
+++ b/test/month_year_dropdown_test.test.js
@@ -9,7 +9,7 @@ import {
   formatDate,
   isAfter,
   registerLocale,
-} from "../src/date_utils.js";
+} from "../src/date_utils.ts";
 import { fi } from "date-fns/locale/fi";
 import onClickOutside from "react-onclickoutside";
 
diff --git a/test/timepicker_test.test.js b/test/timepicker_test.test.js
index 132c7f2b..34e5fbab 100644
--- a/test/timepicker_test.test.js
+++ b/test/timepicker_test.test.js
@@ -1,6 +1,6 @@
 import React from "react";
 import DatePicker from "../src/index.jsx";
-import { newDate, formatDate } from "../src/date_utils.js";
+import { newDate, formatDate } from "../src/date_utils.ts";
 import { getKey } from "./test_utils.js";
 import { render, fireEvent, waitFor } from "@testing-library/react";
 
diff --git a/test/year_dropdown_options_test.test.js b/test/year_dropdown_options_test.test.js
index 72704163..e43f50b5 100644
--- a/test/year_dropdown_options_test.test.js
+++ b/test/year_dropdown_options_test.test.js
@@ -1,7 +1,7 @@
 import React from "react";
 import YearDropdownOptions from "../src/year_dropdown_options.jsx";
 import { render, fireEvent } from "@testing-library/react";
-import * as utils from "../src/date_utils.js";
+import * as utils from "../src/date_utils.ts";
 import onClickOutside from "react-onclickoutside";
 
 describe("YearDropdownOptions", () => {
diff --git a/test/year_dropdown_test.test.js b/test/year_dropdown_test.test.js
index 0c6d7c49..e51e2219 100644
--- a/test/year_dropdown_test.test.js
+++ b/test/year_dropdown_test.test.js
@@ -2,7 +2,7 @@ import React from "react";
 import range from "lodash/range";
 import YearDropdown from "../src/year_dropdown.jsx";
 import { render, fireEvent } from "@testing-library/react";
-import { newDate } from "../src/date_utils.js";
+import { newDate } from "../src/date_utils.ts";
 
 describe("YearDropdown", () => {
   let yearDropdown;
diff --git a/test/year_picker_test.test.js b/test/year_picker_test.test.js
index 6b58e57a..2734b1e0 100644
--- a/test/year_picker_test.test.js
+++ b/test/year_picker_test.test.js
@@ -2,7 +2,7 @@ import React from "react";
 import DatePicker from "../src/index.jsx";
 import Year from "../src/year.jsx";
 import { render, fireEvent } from "@testing-library/react";
-import * as utils from "../src/date_utils.js";
+import * as utils from "../src/date_utils.ts";
 import { getKey } from "./test_utils.js";
 
 describe("YearPicker", () => {