Repository: Hacker0x01/react-datepicker
Pull request #4766 (2024-05-05 14:13:32)

[typescript-migration]  calendar.jsx and index.jsx
## name: Migrate calendar.jsx and index.jsx
## Description
**Linked issue**: #4700

**Changes** `calendar.jsx` and `index.jsx` was migrated to ts

## 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.



martijnrusschen: @yuki0410-dev anything needed to complete this?
yuki0410-dev: @martijnrusschen 
I am working with index.jsx. There is some rework to be done for consistency, etc. I will release the draft after index.js is complete.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4766?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.92%. Comparing base [(`0a1bed3`)](https://app.codecov.io/gh/Hacker0x01/react-datepicker/commit/0a1bed338fee9faf9e283db9772b4bfa163db094?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) to head [(`434c39f`)](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4766?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01).

> :exclamation: Current head 434c39f differs from pull request most recent head 3bc7937. Consider uploading reports for the commit 3bc7937 to get more accurate results


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #4766      +/-   ##
==========================================
- Coverage   96.35%   94.92%   -1.43%     
==========================================
  Files           8        7       -1     
  Lines         959      591     -368     
  Branches      442      279     -163     
==========================================
- Hits          924      561     -363     
+ Misses         35       30       -5     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4766?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01).

mirus-ua: I noticed a lot of changes that replaced `randomProp?: type` with `randomProp?: type | null`. IMO, it is better to replace `null` with `undefined` instead of making all the types wider. 
yuki0410-dev: @mirus-ua 
I am glad for your advice.
I agree with unifying the internal types to undefined or null.
However, since the original selected and other props were of type `Date | null | undefined`, we have modified the related props to `Date | null | undefined` to maintain compatibility.
If you have any particular concerns, it would be appreciated if you could comment on them in the `File changes tab`.
yuki0410-dev: @mirus-ua 
I am struggling with the part of index.ts where the onChange type changes due to selectsRange,selectsMultiple Props.
Any good advice would be appreciated.
In my current code, I am using conditional type with reference to @types/react-datepicker, but the type inference does not work well at the point where onChage is called.
Also, I defined some (conditional type) props using `type =` and tried to inherit them in the interface, but I get a ts(2312) error.
Otherwise, if I declare Props with type instead of interface, the inference works correctly, but it is not consistent because other Props are declared with interface.

Translated with DeepL.com (free version)
mirus-ua: > @mirus-ua I am struggling with the part of index.ts where the onChange type changes due to selectsRange,selectsMultiple Props. Any good advice would be appreciated. In my current code, I am using conditional type with reference to @types/react-datepicker, but the type inference does not work well at the point where onChage is called. Also, I defined some (conditional type) props using `type =` and tried to inherit them in the interface, but I get a ts(2312) error. Otherwise, if I declare Props with type instead of interface, the inference works correctly, but it is not consistent because other Props are declared with interface.

Conditional types work well only with `type` notation, so if the rest is OK and the only concern is consistency, so I suggest migrating to types instead of interfaces, I don't think that someone will be against it (btw I pick type over interface almost always) 
martijnrusschen: @mirus-ua can you review this PR as well?
yuki0410-dev: > `prop: Type | null | undefined` can be simplified to `prop?: Type | null`

I use `args: Type | undefined` and `args?: Type` explicitly.
The former always requires an argument at call time, whereas the latter does not raise an error if you forget to write the argument.
Since Optional is not a mere shorthand for union type with undefined, we believe that it should be used with the best of intentions.

Also, as for the `prop: Type | null | undefined` type, `@types/react-datepickr` used `Date | null | undefined` for selected props, etc., so we do so to maintain compatibility.
And the part where the value is passed is of a similar type.

Patch:
diff --git a/package.json b/package.json
index a170273e..64d292bb 100644
--- a/package.json
+++ b/package.json
@@ -10,23 +10,9 @@
   "module": "dist/es/index.js",
   "unpkg": "dist/react-datepicker.min.js",
   "style": "dist/react-datepicker.min.css",
-  "files": [
-    "*.md",
-    "dist",
-    "lib",
-    "es",
-    "src/stylesheets"
-  ],
-  "sideEffects": [
-    "**/*.css"
-  ],
-  "keywords": [
-    "react",
-    "datepicker",
-    "calendar",
-    "date",
-    "react-component"
-  ],
+  "files": ["*.md", "dist", "lib", "es", "src/stylesheets"],
+  "sideEffects": ["**/*.css"],
+  "keywords": ["react", "datepicker", "calendar", "date", "react-component"],
   "repository": {
     "type": "git",
     "url": "git://github.com/Hacker0x01/react-datepicker.git"
@@ -93,7 +79,7 @@
     "react-onclickoutside": "^6.13.0"
   },
   "scripts": {
-    "eslint": "eslint --ext .js,.jsx src test",
+    "eslint": "eslint --ext .js,.jsx,.ts,.tsx src test",
     "precommit": "lint-staged --allow-empty",
     "sass-lint": "stylelint 'src/stylesheets/*.scss'",
     "lint": "yarn run eslint && yarn run sass-lint",
@@ -117,10 +103,7 @@
     "prepare": "husky install"
   },
   "lint-staged": {
-    "*.{js,jsx,json,css,scss,md}": [
-      "prettier --write",
-      "git add"
-    ]
+    "*.{js,jsx,ts,tsx,json,css,scss,md}": ["prettier --write", "git add"]
   },
   "packageManager": "yarn@1.22.17"
 }
diff --git a/rollup.config.mjs b/rollup.config.mjs
index 3b8035cc..31b9e5c5 100644
--- a/rollup.config.mjs
+++ b/rollup.config.mjs
@@ -57,7 +57,7 @@ const migrateRollup2to3OutputOptions = {
 
 /** @type {import('rollup').RollupOptions} */
 const config = {
-  input: "src/index.jsx",
+  input: "src/index.tsx",
   output: [
     {
       file: pkg.browser,
diff --git a/src/@types/react-onclickoutside/index.d.ts b/src/@types/react-onclickoutside/index.d.ts
new file mode 100644
index 00000000..317cadba
--- /dev/null
+++ b/src/@types/react-onclickoutside/index.d.ts
@@ -0,0 +1,7 @@
+import { findDOMNode } from "react-dom";
+
+declare module "react-onclickoutside" {
+  interface WrapperInstance<P, C> {
+    componentNode: ReturnType<typeof findDOMNode>;
+  }
+}
diff --git a/src/calendar.jsx b/src/calendar.tsx
similarity index 62%
rename from src/calendar.jsx
rename to src/calendar.tsx
index b4ddbfd3..8b7eba1f 100644
--- a/src/calendar.jsx
+++ b/src/calendar.tsx
@@ -5,8 +5,7 @@ import Month from "./month";
 import Time from "./time";
 import Year from "./year";
 import InputTime from "./input_time";
-import React from "react";
-import PropTypes from "prop-types";
+import React, { Component, createRef } from "react";
 import { clsx } from "clsx";
 import CalendarContainer from "./calendar_container";
 import {
@@ -45,7 +44,28 @@ import {
   getYearsPeriod,
   DEFAULT_YEAR_ITEM_NUMBER,
   getMonthInLocale,
+  // eslint-disable-next-line unused-imports/no-unused-imports
+  type Locale,
 } from "./date_utils";
+import { Day } from "date-fns/types";
+
+interface YearDropdownProps
+  extends React.ComponentPropsWithoutRef<typeof YearDropdown> {}
+
+interface MonthDropdownProps
+  extends React.ComponentPropsWithoutRef<typeof MonthDropdown> {}
+
+interface MonthYearDropdownProps
+  extends React.ComponentPropsWithoutRef<typeof MonthYearDropdown> {}
+
+interface YearProps extends React.ComponentPropsWithoutRef<typeof Year> {}
+
+interface MonthProps extends React.ComponentPropsWithoutRef<typeof Month> {}
+
+interface TimeProps extends React.ComponentPropsWithoutRef<typeof Time> {}
+
+interface InputTimeProps
+  extends React.ComponentPropsWithoutRef<typeof InputTime> {}
 
 const DROPDOWN_FOCUS_CLASSNAMES = [
   "react-datepicker__year-select",
@@ -53,17 +73,155 @@ const DROPDOWN_FOCUS_CLASSNAMES = [
   "react-datepicker__month-year-select",
 ];
 
-const isDropdownSelect = (element = {}) => {
+const isDropdownSelect = (element: HTMLDivElement) => {
   const classNames = (element.className || "").split(/\s+/);
   return DROPDOWN_FOCUS_CLASSNAMES.some(
     (testClassname) => classNames.indexOf(testClassname) >= 0,
   );
 };
 
-export default class Calendar extends React.Component {
-  static get defaultProps() {
+export interface ReactDatePickerCustomHeaderProps {
+  date: CalendarState["date"];
+  customHeaderCount: number;
+  monthDate: Date;
+  changeMonth: (month: number) => void;
+  changeYear: (year: number) => void;
+  decreaseMonth: VoidFunction;
+  increaseMonth: VoidFunction;
+  decreaseYear: VoidFunction;
+  increaseYear: VoidFunction;
+  prevMonthButtonDisabled: boolean;
+  nextMonthButtonDisabled: boolean;
+  prevYearButtonDisabled: boolean;
+  nextYearButtonDisabled: boolean;
+}
+
+type CalendarProps = React.PropsWithChildren &
+  Omit<
+    YearDropdownProps,
+    "date" | "onChange" | "year" | "minDate" | "maxDate"
+  > &
+  Omit<MonthDropdownProps, "month" | "onChange"> &
+  Omit<MonthYearDropdownProps, "date" | "onChange" | "minDate" | "maxDate"> &
+  Omit<
+    YearProps,
+    | "onDayClick"
+    | "selectingDate"
+    | "clearSelectingDate"
+    | "onYearMouseEnter"
+    | "onYearMouseLeave"
+    | "minDate"
+    | "maxDate"
+  > &
+  Omit<
+    MonthProps,
+    | "ariaLabelPrefix"
+    | "onChange"
+    | "day"
+    | "onDayClick"
+    | "handleOnKeyDown"
+    | "handleOnMonthKeyDown"
+    | "onDayMouseEnter"
+    | "onMouseLeave"
+    | "orderInDisplay"
+    | "monthShowsDuplicateDaysEnd"
+    | "monthShowsDuplicateDaysStart"
+    | "minDate"
+    | "maxDate"
+  > &
+  Omit<TimeProps, "onChange" | "format" | "intervals" | "monthRef"> &
+  Omit<InputTimeProps, "date" | "timeString" | "onChange"> & {
+    className?: string;
+    container?: React.ElementType;
+    showYearPicker?: boolean;
+    showMonthYearPicker?: boolean;
+    showQuarterYearPicker?: boolean;
+    showTimeSelect?: boolean;
+    showTimeInput?: boolean;
+    showYearDropdown?: boolean;
+    showMonthDropdown?: boolean;
+    yearItemNumber: number;
+    useWeekdaysShort?: boolean;
+    forceShowMonthNavigation?: boolean;
+    showDisabledMonthNavigation?: boolean;
+    formatWeekDay?: (date: string) => string;
+    onDropdownFocus?: (event: React.FocusEvent<HTMLDivElement>) => void;
+    calendarStartDay?: Day;
+    weekDayClassName?: (date: Date) => string;
+    onMonthChange?: (date: Date) => void;
+    onYearChange?: (date: Date) => void;
+    onDayMouseEnter?: (date: Date) => void;
+    onMonthMouseLeave?: VoidFunction;
+    weekLabel?: string;
+    onClickOutside: React.MouseEventHandler<HTMLElement>;
+    previousMonthButtonLabel?: React.ReactNode;
+    previousYearButtonLabel?: string;
+    previousMonthAriaLabel?: string;
+    previousYearAriaLabel?: string;
+    nextMonthButtonLabel?: React.ReactNode;
+    nextYearButtonLabel?: string;
+    nextMonthAriaLabel?: string;
+    nextYearAriaLabel?: string;
+    showPreviousMonths?: boolean;
+    monthsShown?: number;
+    monthSelectedIn?: number;
+    onSelect: (
+      day: Date,
+      event?:
+        | React.MouseEvent<HTMLDivElement>
+        | React.KeyboardEvent<HTMLDivElement>,
+      monthSelectedIn?: number,
+    ) => void;
+    renderCustomHeader?: (
+      props: ReactDatePickerCustomHeaderProps,
+    ) => JSX.Element;
+    onYearMouseEnter?: YearProps["onYearMouseEnter"];
+    onYearMouseLeave?: YearProps["onYearMouseLeave"];
+    monthAriaLabelPrefix?: MonthProps["ariaLabelPrefix"];
+    handleOnDayKeyDown: MonthProps["handleOnKeyDown"];
+    handleOnKeyDown?: (
+      event:
+        | React.KeyboardEvent<HTMLDivElement>
+        | React.KeyboardEvent<HTMLLIElement>
+        | React.KeyboardEvent<HTMLButtonElement>,
+    ) => void;
+    onTimeChange?: TimeProps["onChange"] | InputTimeProps["onChange"];
+    timeFormat?: TimeProps["format"];
+    timeIntervals: TimeProps["intervals"];
+  } & (
+    | ({
+        showMonthYearDropdown: true;
+      } & Pick<MonthYearDropdownProps, "maxDate" | "minDate">)
+    | ({
+        showMonthYearDropdown?: never;
+      } & Pick<YearDropdownProps, "maxDate" | "minDate"> &
+        Pick<YearProps, "maxDate" | "minDate"> &
+        Pick<MonthProps, "maxDate" | "minDate">)
+  );
+
+interface CalendarState
+  extends Pick<YearProps, "selectingDate">,
+    Pick<MonthProps, "selectingDate"> {
+  date: Required<YearProps>["date"];
+  monthContainer: TimeProps["monthRef"];
+  isRenderAriaLiveMessage: boolean;
+}
+
+export default class Calendar extends Component<CalendarProps, CalendarState> {
+  static get defaultProps(): Required<
+    Pick<
+      CalendarProps,
+      | "monthsShown"
+      | "forceShowMonthNavigation"
+      | "timeCaption"
+      | "previousYearButtonLabel"
+      | "nextYearButtonLabel"
+      | "previousMonthButtonLabel"
+      | "nextMonthButtonLabel"
+      | "yearItemNumber"
+    >
+  > {
     return {
-      onDropdownFocus: () => {},
       monthsShown: 1,
       forceShowMonthNavigation: false,
       timeCaption: "Time",
@@ -71,166 +229,19 @@ export default class Calendar extends React.Component {
       nextYearButtonLabel: "Next Year",
       previousMonthButtonLabel: "Previous Month",
       nextMonthButtonLabel: "Next Month",
-      customTimeInput: null,
       yearItemNumber: DEFAULT_YEAR_ITEM_NUMBER,
     };
   }
 
-  static propTypes = {
-    adjustDateOnChange: PropTypes.bool,
-    chooseDayAriaLabelPrefix: PropTypes.string,
-    className: PropTypes.string,
-    children: PropTypes.node,
-    container: PropTypes.func,
-    dateFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.array])
-      .isRequired,
-    dayClassName: PropTypes.func,
-    weekDayClassName: PropTypes.func,
-    disabledDayAriaLabelPrefix: PropTypes.string,
-    monthClassName: PropTypes.func,
-    timeClassName: PropTypes.func,
-    yearClassName: PropTypes.func,
-    disabledKeyboardNavigation: PropTypes.bool,
-    calendarStartDay: PropTypes.number,
-    dropdownMode: PropTypes.oneOf(["scroll", "select"]),
-    endDate: PropTypes.instanceOf(Date),
-    excludeDates: PropTypes.arrayOf(
-      PropTypes.oneOfType([
-        PropTypes.instanceOf(Date),
-        PropTypes.shape({
-          date: PropTypes.instanceOf(Date).isRequired,
-          message: PropTypes.string,
-        }),
-      ]),
-    ),
-    excludeDateIntervals: PropTypes.arrayOf(
-      PropTypes.shape({
-        start: PropTypes.instanceOf(Date),
-        end: PropTypes.instanceOf(Date),
-      }),
-    ),
-    filterDate: PropTypes.func,
-    fixedHeight: PropTypes.bool,
-    formatWeekNumber: PropTypes.func,
-    highlightDates: PropTypes.instanceOf(Map),
-    holidays: PropTypes.instanceOf(Map),
-    includeDates: PropTypes.array,
-    includeDateIntervals: PropTypes.arrayOf(
-      PropTypes.shape({
-        start: PropTypes.instanceOf(Date),
-        end: PropTypes.instanceOf(Date),
-      }),
-    ),
-    includeTimes: PropTypes.array,
-    injectTimes: PropTypes.array,
-    inline: PropTypes.bool,
-    shouldFocusDayInline: PropTypes.bool,
-    locale: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.shape({ locale: PropTypes.object }),
-    ]),
-    maxDate: PropTypes.instanceOf(Date),
-    minDate: PropTypes.instanceOf(Date),
-    monthsShown: PropTypes.number,
-    monthSelectedIn: PropTypes.number,
-    nextMonthAriaLabel: PropTypes.string,
-    nextYearAriaLabel: PropTypes.string,
-    onClickOutside: PropTypes.func.isRequired,
-    onMonthChange: PropTypes.func,
-    onYearChange: PropTypes.func,
-    forceShowMonthNavigation: PropTypes.bool,
-    onDropdownFocus: PropTypes.func,
-    onSelect: PropTypes.func.isRequired,
-    onWeekSelect: PropTypes.func,
-    showTimeSelect: PropTypes.bool,
-    showTimeInput: PropTypes.bool,
-    showMonthYearPicker: PropTypes.bool,
-    showFullMonthYearPicker: PropTypes.bool,
-    showTwoColumnMonthYearPicker: PropTypes.bool,
-    showFourColumnMonthYearPicker: PropTypes.bool,
-    showYearPicker: PropTypes.bool,
-    showQuarterYearPicker: PropTypes.bool,
-    showWeekPicker: PropTypes.bool,
-    showTimeSelectOnly: PropTypes.bool,
-    timeFormat: PropTypes.string,
-    timeIntervals: PropTypes.number,
-    onTimeChange: PropTypes.func,
-    timeInputLabel: PropTypes.string,
-    minTime: PropTypes.instanceOf(Date),
-    maxTime: PropTypes.instanceOf(Date),
-    excludeTimes: PropTypes.array,
-    filterTime: PropTypes.func,
-    timeCaption: PropTypes.string,
-    openToDate: PropTypes.instanceOf(Date),
-    peekNextMonth: PropTypes.bool,
-    previousMonthAriaLabel: PropTypes.string,
-    previousYearAriaLabel: PropTypes.string,
-    scrollableYearDropdown: PropTypes.bool,
-    scrollableMonthYearDropdown: PropTypes.bool,
-    preSelection: PropTypes.instanceOf(Date),
-    selected: PropTypes.instanceOf(Date),
-    selectsEnd: PropTypes.bool,
-    selectsStart: PropTypes.bool,
-    selectsRange: PropTypes.bool,
-    selectsDisabledDaysInRange: PropTypes.bool,
-    selectsMultiple: PropTypes.bool,
-    selectedDates: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
-    showMonthDropdown: PropTypes.bool,
-    showPreviousMonths: PropTypes.bool,
-    showMonthYearDropdown: PropTypes.bool,
-    showWeekNumbers: PropTypes.bool,
-    showYearDropdown: PropTypes.bool,
-    startDate: PropTypes.instanceOf(Date),
-    todayButton: PropTypes.string,
-    useWeekdaysShort: PropTypes.bool,
-    formatWeekDay: PropTypes.func,
-    withPortal: PropTypes.bool,
-    weekLabel: PropTypes.string,
-    yearItemNumber: PropTypes.number,
-    yearDropdownItemNumber: PropTypes.number,
-    setOpen: PropTypes.func,
-    shouldCloseOnSelect: PropTypes.bool,
-    useShortMonthInDropdown: PropTypes.bool,
-    showDisabledMonthNavigation: PropTypes.bool,
-    previousMonthButtonLabel: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.node,
-    ]),
-    nextMonthButtonLabel: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.node,
-    ]),
-    previousYearButtonLabel: PropTypes.string,
-    nextYearButtonLabel: PropTypes.string,
-    renderCustomHeader: PropTypes.func,
-    renderDayContents: PropTypes.func,
-    renderMonthContent: PropTypes.func,
-    renderQuarterContent: PropTypes.func,
-    renderYearContent: PropTypes.func,
-    usePointerEvent: PropTypes.bool,
-    onDayMouseEnter: PropTypes.func,
-    onMonthMouseLeave: PropTypes.func,
-    onYearMouseEnter: PropTypes.func,
-    onYearMouseLeave: PropTypes.func,
-    showPopperArrow: PropTypes.bool,
-    handleOnKeyDown: PropTypes.func,
-    handleOnDayKeyDown: PropTypes.func,
-    isInputFocused: PropTypes.bool,
-    customTimeInput: PropTypes.element,
-    weekAriaLabelPrefix: PropTypes.string,
-    monthAriaLabelPrefix: PropTypes.string,
-    setPreSelection: PropTypes.func,
-  };
-
-  constructor(props) {
+  constructor(props: Readonly<CalendarProps>) {
     super(props);
 
-    this.containerRef = React.createRef();
+    this.containerRef = createRef<HTMLDivElement>();
 
     this.state = {
       date: this.getDateInView(),
-      selectingDate: null,
-      monthContainer: null,
+      selectingDate: undefined,
+      monthContainer: undefined,
       isRenderAriaLiveMessage: false,
     };
   }
@@ -241,13 +252,13 @@ export default class Calendar extends React.Component {
     // setState here so height is given after final component
     // layout is rendered
     if (this.props.showTimeSelect) {
-      this.assignMonthContainer = (() => {
+      this.assignMonthContainer = ((): void => {
         this.setState({ monthContainer: this.monthContainer });
       })();
     }
   }
 
-  componentDidUpdate(prevProps) {
+  componentDidUpdate(prevProps: Readonly<CalendarProps>) {
     if (
       this.props.preSelection &&
       (!isSameDay(this.props.preSelection, prevProps.preSelection) ||
@@ -273,21 +284,27 @@ export default class Calendar extends React.Component {
     }
   }
 
-  handleClickOutside = (event) => {
+  containerRef: React.RefObject<HTMLDivElement>;
+
+  monthContainer: CalendarState["monthContainer"] = undefined;
+
+  assignMonthContainer: void | undefined;
+
+  handleClickOutside = (event: React.MouseEvent<HTMLElement>): void => {
     this.props.onClickOutside(event);
   };
 
-  setClickOutsideRef = () => {
+  setClickOutsideRef = (): HTMLDivElement | null => {
     return this.containerRef.current;
   };
 
-  handleDropdownFocus = (event) => {
+  handleDropdownFocus = (event: React.FocusEvent<HTMLDivElement>): void => {
     if (isDropdownSelect(event.target)) {
-      this.props.onDropdownFocus();
+      this.props.onDropdownFocus?.(event);
     }
   };
 
-  getDateInView = () => {
+  getDateInView = (): Date => {
     const { preSelection, selected, openToDate } = this.props;
     const minDate = getEffectiveMinDate(this.props);
     const maxDate = getEffectiveMaxDate(this.props);
@@ -305,7 +322,7 @@ export default class Calendar extends React.Component {
     return current;
   };
 
-  increaseMonth = () => {
+  increaseMonth = (): void => {
     this.setState(
       ({ date }) => ({
         date: addMonths(date, 1),
@@ -314,7 +331,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  decreaseMonth = () => {
+  decreaseMonth = (): void => {
     this.setState(
       ({ date }) => ({
         date: subMonths(date, 1),
@@ -323,31 +340,43 @@ export default class Calendar extends React.Component {
     );
   };
 
-  handleDayClick = (day, event, monthSelectedIn) => {
+  handleDayClick = (
+    day: Date,
+    event:
+      | React.MouseEvent<HTMLDivElement>
+      | React.KeyboardEvent<HTMLDivElement>,
+    monthSelectedIn?: number,
+  ): void => {
     this.props.onSelect(day, event, monthSelectedIn);
     this.props.setPreSelection && this.props.setPreSelection(day);
   };
 
-  handleDayMouseEnter = (day) => {
+  handleDayMouseEnter = (day: Date): void => {
     this.setState({ selectingDate: day });
     this.props.onDayMouseEnter && this.props.onDayMouseEnter(day);
   };
 
-  handleMonthMouseLeave = () => {
-    this.setState({ selectingDate: null });
+  handleMonthMouseLeave = (): void => {
+    this.setState({ selectingDate: undefined });
     this.props.onMonthMouseLeave && this.props.onMonthMouseLeave();
   };
 
-  handleYearMouseEnter = (event, year) => {
+  handleYearMouseEnter = (
+    event: React.MouseEvent<HTMLDivElement>,
+    year: number,
+  ): void => {
     this.setState({ selectingDate: setYear(newDate(), year) });
     !!this.props.onYearMouseEnter && this.props.onYearMouseEnter(event, year);
   };
 
-  handleYearMouseLeave = (event, year) => {
+  handleYearMouseLeave = (
+    event: React.MouseEvent<HTMLDivElement>,
+    year: number,
+  ): void => {
     !!this.props.onYearMouseLeave && this.props.onYearMouseLeave(event, year);
   };
 
-  handleYearChange = (date) => {
+  handleYearChange = (date: Date): void => {
     if (this.props.onYearChange) {
       this.props.onYearChange(date);
       this.setState({ isRenderAriaLiveMessage: true });
@@ -364,7 +393,7 @@ export default class Calendar extends React.Component {
     this.props.setPreSelection && this.props.setPreSelection(date);
   };
 
-  handleMonthChange = (date) => {
+  handleMonthChange = (date: Date): void => {
     this.handleCustomMonthChange(date);
     if (this.props.adjustDateOnChange) {
       if (this.props.onSelect) {
@@ -378,37 +407,37 @@ export default class Calendar extends React.Component {
     this.props.setPreSelection && this.props.setPreSelection(date);
   };
 
-  handleCustomMonthChange = (date) => {
+  handleCustomMonthChange = (date: Date): void => {
     if (this.props.onMonthChange) {
       this.props.onMonthChange(date);
       this.setState({ isRenderAriaLiveMessage: true });
     }
   };
 
-  handleMonthYearChange = (date) => {
+  handleMonthYearChange = (date: Date): void => {
     this.handleYearChange(date);
     this.handleMonthChange(date);
   };
 
-  changeYear = (year) => {
+  changeYear = (year: number): void => {
     this.setState(
       ({ date }) => ({
-        date: setYear(date, year),
+        date: setYear(date, Number(year)),
       }),
       () => this.handleYearChange(this.state.date),
     );
   };
 
-  changeMonth = (month) => {
+  changeMonth = (month: number): void => {
     this.setState(
       ({ date }) => ({
-        date: setMonth(date, month),
+        date: setMonth(date, Number(month)),
       }),
       () => this.handleMonthChange(this.state.date),
     );
   };
 
-  changeMonthYear = (monthYear) => {
+  changeMonthYear = (monthYear: Date): void => {
     this.setState(
       ({ date }) => ({
         date: setYear(setMonth(date, getMonth(monthYear)), getYear(monthYear)),
@@ -417,14 +446,14 @@ export default class Calendar extends React.Component {
     );
   };
 
-  header = (date = this.state.date) => {
+  header = (date: Date = this.state.date): JSX.Element[] => {
     const startOfWeek = getStartOfWeek(
       date,
       this.props.locale,
       this.props.calendarStartDay,
     );
 
-    const dayNames = [];
+    const dayNames: JSX.Element[] = [];
     if (this.props.showWeekNumbers) {
       dayNames.push(
         <div key="W" className="react-datepicker__day-name">
@@ -454,7 +483,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  formatWeekday = (day, locale) => {
+  formatWeekday = (day: Date, locale?: Locale): string => {
     if (this.props.formatWeekDay) {
       return getFormattedWeekdayInLocale(day, this.props.formatWeekDay, locale);
     }
@@ -463,7 +492,7 @@ export default class Calendar extends React.Component {
       : getWeekdayMinInLocale(day, locale);
   };
 
-  decreaseYear = () => {
+  decreaseYear = (): void => {
     this.setState(
       ({ date }) => ({
         date: subYears(
@@ -475,11 +504,11 @@ export default class Calendar extends React.Component {
     );
   };
 
-  clearSelectingDate = () => {
-    this.setState({ selectingDate: null });
+  clearSelectingDate = (): void => {
+    this.setState({ selectingDate: undefined });
   };
 
-  renderPreviousButton = () => {
+  renderPreviousButton = (): JSX.Element | void => {
     if (this.props.renderCustomHeader) {
       return;
     }
@@ -522,7 +551,8 @@ export default class Calendar extends React.Component {
       "react-datepicker__navigation--previous",
     ];
 
-    let clickHandler = this.decreaseMonth;
+    let clickHandler: React.MouseEventHandler<HTMLButtonElement> | undefined =
+      this.decreaseMonth;
 
     if (
       this.props.showMonthYearPicker ||
@@ -534,7 +564,7 @@ export default class Calendar extends React.Component {
 
     if (allPrevDaysDisabled && this.props.showDisabledMonthNavigation) {
       classes.push("react-datepicker__navigation--previous--disabled");
-      clickHandler = null;
+      clickHandler = undefined;
     }
 
     const isForYear =
@@ -570,7 +600,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  increaseYear = () => {
+  increaseYear = (): void => {
     this.setState(
       ({ date }) => ({
         date: addYears(
@@ -582,12 +612,12 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderNextButton = () => {
+  renderNextButton = (): JSX.Element | void => {
     if (this.props.renderCustomHeader) {
       return;
     }
 
-    let allNextDaysDisabled;
+    let allNextDaysDisabled: boolean;
     switch (true) {
       case this.props.showMonthYearPicker:
         allNextDaysDisabled = yearDisabledAfter(this.state.date, this.props);
@@ -612,7 +642,7 @@ export default class Calendar extends React.Component {
       return;
     }
 
-    const classes = [
+    const classes: string[] = [
       "react-datepicker__navigation",
       "react-datepicker__navigation--next",
     ];
@@ -627,7 +657,8 @@ export default class Calendar extends React.Component {
       classes.push("react-datepicker__navigation--next--with-today-button");
     }
 
-    let clickHandler = this.increaseMonth;
+    let clickHandler: React.MouseEventHandler<HTMLButtonElement> | undefined =
+      this.increaseMonth;
 
     if (
       this.props.showMonthYearPicker ||
@@ -639,7 +670,7 @@ export default class Calendar extends React.Component {
 
     if (allNextDaysDisabled && this.props.showDisabledMonthNavigation) {
       classes.push("react-datepicker__navigation--next--disabled");
-      clickHandler = null;
+      clickHandler = undefined;
     }
 
     const isForYear =
@@ -674,7 +705,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderCurrentMonth = (date = this.state.date) => {
+  renderCurrentMonth = (date: Date = this.state.date): JSX.Element => {
     const classes = ["react-datepicker__current-month"];
 
     if (this.props.showYearDropdown) {
@@ -693,80 +724,72 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderYearDropdown = (overrideHide = false) => {
+  renderYearDropdown = (
+    overrideHide: boolean = false,
+  ): JSX.Element | undefined => {
     if (!this.props.showYearDropdown || overrideHide) {
       return;
     }
     return (
       <YearDropdown
-        adjustDateOnChange={this.props.adjustDateOnChange}
+        {...this.props}
         date={this.state.date}
-        onSelect={this.props.onSelect}
-        setOpen={this.props.setOpen}
-        dropdownMode={this.props.dropdownMode}
         onChange={this.changeYear}
-        minDate={this.props.minDate}
-        maxDate={this.props.maxDate}
         year={getYear(this.state.date)}
-        scrollableYearDropdown={this.props.scrollableYearDropdown}
-        yearDropdownItemNumber={this.props.yearDropdownItemNumber}
       />
     );
   };
 
-  renderMonthDropdown = (overrideHide = false) => {
+  renderMonthDropdown = (
+    overrideHide: boolean = false,
+  ): JSX.Element | undefined => {
     if (!this.props.showMonthDropdown || overrideHide) {
       return;
     }
     return (
       <MonthDropdown
-        dropdownMode={this.props.dropdownMode}
-        locale={this.props.locale}
-        onChange={this.changeMonth}
+        {...this.props}
         month={getMonth(this.state.date)}
-        useShortMonthInDropdown={this.props.useShortMonthInDropdown}
+        onChange={this.changeMonth}
       />
     );
   };
 
-  renderMonthYearDropdown = (overrideHide = false) => {
+  renderMonthYearDropdown = (
+    overrideHide: boolean = false,
+  ): JSX.Element | undefined => {
     if (!this.props.showMonthYearDropdown || overrideHide) {
       return;
     }
     return (
       <MonthYearDropdown
-        dropdownMode={this.props.dropdownMode}
-        locale={this.props.locale}
-        dateFormat={this.props.dateFormat}
-        onChange={this.changeMonthYear}
-        minDate={this.props.minDate}
-        maxDate={this.props.maxDate}
+        {...this.props}
         date={this.state.date}
-        scrollableMonthYearDropdown={this.props.scrollableMonthYearDropdown}
+        onChange={this.changeMonthYear}
       />
     );
   };
 
-  handleTodayButtonClick = (e) => {
-    this.props.onSelect(getStartOfToday(), e);
+  handleTodayButtonClick = (event: React.MouseEvent<HTMLDivElement>): void => {
+    this.props.onSelect(getStartOfToday(), event);
     this.props.setPreSelection && this.props.setPreSelection(getStartOfToday());
   };
 
-  renderTodayButton = () => {
+  renderTodayButton = (): JSX.Element | undefined => {
     if (!this.props.todayButton || this.props.showTimeSelectOnly) {
       return;
     }
     return (
       <div
         className="react-datepicker__today-button"
-        onClick={(e) => this.handleTodayButtonClick(e)}
+        onClick={this.handleTodayButtonClick}
       >
         {this.props.todayButton}
       </div>
     );
   };
 
-  renderDefaultHeader = ({ monthDate, i }) => (
+  renderDefaultHeader = ({ monthDate, i }: { monthDate: Date; i: number }) => (
     <div
       className={`react-datepicker__header ${
         this.props.showTimeSelect
@@ -789,7 +812,7 @@ export default class Calendar extends React.Component {
     </div>
   );
 
-  renderCustomHeader = (headerArgs = {}) => {
+  renderCustomHeader = (headerArgs: { monthDate: Date; i: number }) => {
     const { monthDate, i } = headerArgs;
 
     if (
@@ -829,7 +852,7 @@ export default class Calendar extends React.Component {
         className="react-datepicker__header react-datepicker__header--custom"
         onFocus={this.props.onDropdownFocus}
       >
-        {this.props.renderCustomHeader({
+        {this.props.renderCustomHeader?.({
           ...this.state,
           customHeaderCount: i,
           monthDate,
@@ -853,7 +876,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderYearHeader = ({ monthDate }) => {
+  renderYearHeader = ({ monthDate }: { monthDate: Date }): JSX.Element => {
     const { showYearPicker, yearItemNumber } = this.props;
     const { startPeriod, endPeriod } = getYearsPeriod(
       monthDate,
@@ -866,7 +889,14 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderHeader = (headerArgs) => {
+  renderHeader = ({
+    monthDate,
+    i = 0,
+  }: {
+    monthDate: Date;
+    i?: number;
+  }): JSX.Element | null => {
+    const headerArgs = { monthDate, i };
     switch (true) {
       case this.props.renderCustomHeader !== undefined:
         return this.renderCustomHeader(headerArgs);
@@ -879,104 +909,51 @@ export default class Calendar extends React.Component {
     }
   };
 
-  renderMonths = () => {
+  renderMonths = (): JSX.Element[] | undefined => {
     if (this.props.showTimeSelectOnly || this.props.showYearPicker) {
       return;
     }
 
-    const monthList = [];
+    const monthList: JSX.Element[] = [];
+    const monthsShown =
+      this.props.monthsShown ?? Calendar.defaultProps.monthsShown;
     const monthsToSubtract = this.props.showPreviousMonths
-      ? this.props.monthsShown - 1
+      ? monthsShown - 1
       : 0;
     const fromMonthDate =
       this.props.showMonthYearPicker || this.props.showQuarterYearPicker
         ? addYears(this.state.date, monthsToSubtract)
         : subMonths(this.state.date, monthsToSubtract);
     const monthSelectedIn = this.props.monthSelectedIn ?? monthsToSubtract;
-    for (let i = 0; i < this.props.monthsShown; ++i) {
+    for (let i = 0; i < monthsShown; ++i) {
       const monthsToAdd = i - monthSelectedIn + monthsToSubtract;
       const monthDate =
         this.props.showMonthYearPicker || this.props.showQuarterYearPicker
           ? addYears(fromMonthDate, monthsToAdd)
           : addMonths(fromMonthDate, monthsToAdd);
       const monthKey = `month-${i}`;
-      const monthShowsDuplicateDaysEnd = i < this.props.monthsShown - 1;
+      const monthShowsDuplicateDaysEnd = i < monthsShown - 1;
       const monthShowsDuplicateDaysStart = i > 0;
       monthList.push(
         <div
           key={monthKey}
           ref={(div) => {
-            this.monthContainer = div;
+            this.monthContainer = div ?? undefined;
           }}
           className="react-datepicker__month-container"
         >
           {this.renderHeader({ monthDate, i })}
           <Month
-            chooseDayAriaLabelPrefix={this.props.chooseDayAriaLabelPrefix}
-            disabledDayAriaLabelPrefix={this.props.disabledDayAriaLabelPrefix}
-            weekAriaLabelPrefix={this.props.weekAriaLabelPrefix}
+            {...this.props}
             ariaLabelPrefix={this.props.monthAriaLabelPrefix}
-            onChange={this.changeMonthYear}
             day={monthDate}
-            dayClassName={this.props.dayClassName}
-            calendarStartDay={this.props.calendarStartDay}
-            monthClassName={this.props.monthClassName}
             onDayClick={this.handleDayClick}
             handleOnKeyDown={this.props.handleOnDayKeyDown}
             handleOnMonthKeyDown={this.props.handleOnKeyDown}
-            usePointerEvent={this.props.usePointerEvent}
             onDayMouseEnter={this.handleDayMouseEnter}
             onMouseLeave={this.handleMonthMouseLeave}
-            onWeekSelect={this.props.onWeekSelect}
             orderInDisplay={i}
-            formatWeekNumber={this.props.formatWeekNumber}
-            locale={this.props.locale}
-            minDate={this.props.minDate}
-            maxDate={this.props.maxDate}
-            excludeDates={this.props.excludeDates}
-            excludeDateIntervals={this.props.excludeDateIntervals}
-            highlightDates={this.props.highlightDates}
-            holidays={this.props.holidays}
             selectingDate={this.state.selectingDate}
-            includeDates={this.props.includeDates}
-            includeDateIntervals={this.props.includeDateIntervals}
-            inline={this.props.inline}
-            shouldFocusDayInline={this.props.shouldFocusDayInline}
-            fixedHeight={this.props.fixedHeight}
-            filterDate={this.props.filterDate}
-            preSelection={this.props.preSelection}
-            setPreSelection={this.props.setPreSelection}
-            selected={this.props.selected}
-            selectsStart={this.props.selectsStart}
-            selectsEnd={this.props.selectsEnd}
-            selectsRange={this.props.selectsRange}
-            selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
-            selectsMultiple={this.props.selectsMultiple}
-            selectedDates={this.props.selectedDates}
-            showWeekNumbers={this.props.showWeekNumbers}
-            startDate={this.props.startDate}
-            endDate={this.props.endDate}
-            peekNextMonth={this.props.peekNextMonth}
-            setOpen={this.props.setOpen}
-            shouldCloseOnSelect={this.props.shouldCloseOnSelect}
-            renderDayContents={this.props.renderDayContents}
-            renderMonthContent={this.props.renderMonthContent}
-            renderQuarterContent={this.props.renderQuarterContent}
-            renderYearContent={this.props.renderYearContent}
-            disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
-            showMonthYearPicker={this.props.showMonthYearPicker}
-            showFullMonthYearPicker={this.props.showFullMonthYearPicker}
-            showTwoColumnMonthYearPicker={
-              this.props.showTwoColumnMonthYearPicker
-            }
-            showFourColumnMonthYearPicker={
-              this.props.showFourColumnMonthYearPicker
-            }
-            showYearPicker={this.props.showYearPicker}
-            showQuarterYearPicker={this.props.showQuarterYearPicker}
-            showWeekPicker={this.props.showWeekPicker}
-            isInputFocused={this.props.isInputFocused}
-            containerRef={this.containerRef}
             monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}
             monthShowsDuplicateDaysStart={monthShowsDuplicateDaysStart}
           />
@@ -986,7 +963,7 @@ export default class Calendar extends React.Component {
     return monthList;
   };
 
-  renderYears = () => {
+  renderYears = (): JSX.Element | undefined => {
     if (this.props.showTimeSelectOnly) {
       return;
     }
@@ -995,73 +972,60 @@ export default class Calendar extends React.Component {
         <div className="react-datepicker__year--container">
           {this.renderHeader({ monthDate: this.state.date })}
           <Year
-            onDayClick={this.handleDayClick}
+            {...this.props}
             selectingDate={this.state.selectingDate}
-            clearSelectingDate={this.clearSelectingDate}
             date={this.state.date}
-            {...this.props}
+            onDayClick={this.handleDayClick}
+            clearSelectingDate={this.clearSelectingDate}
             onYearMouseEnter={this.handleYearMouseEnter}
             onYearMouseLeave={this.handleYearMouseLeave}
           />
         </div>
       );
     }
+    return;
   };
 
-  renderTimeSection = () => {
+  renderTimeSection = (): JSX.Element | undefined => {
     if (
       this.props.showTimeSelect &&
       (this.state.monthContainer || this.props.showTimeSelectOnly)
     ) {
       return (
         <Time
-          selected={this.props.selected}
-          openToDate={this.props.openToDate}
+          {...this.props}
           onChange={this.props.onTimeChange}
-          timeClassName={this.props.timeClassName}
           format={this.props.timeFormat}
-          includeTimes={this.props.includeTimes}
           intervals={this.props.timeIntervals}
-          minTime={this.props.minTime}
-          maxTime={this.props.maxTime}
-          excludeTimes={this.props.excludeTimes}
-          filterTime={this.props.filterTime}
-          timeCaption={this.props.timeCaption}
-          todayButton={this.props.todayButton}
-          showMonthDropdown={this.props.showMonthDropdown}
-          showMonthYearDropdown={this.props.showMonthYearDropdown}
-          showYearDropdown={this.props.showYearDropdown}
-          withPortal={this.props.withPortal}
           monthRef={this.state.monthContainer}
-          injectTimes={this.props.injectTimes}
-          locale={this.props.locale}
-          handleOnKeyDown={this.props.handleOnKeyDown}
-          showTimeSelectOnly={this.props.showTimeSelectOnly}
         />
       );
     }
+    return;
   };
 
-  renderInputTimeSection = () => {
-    const time = new Date(this.props.selected);
-    const timeValid = isValid(time) && Boolean(this.props.selected);
+  renderInputTimeSection = (): JSX.Element | undefined => {
+    const time = this.props.selected
+      ? new Date(this.props.selected)
+      : undefined;
+    const timeValid = time && isValid(time) && Boolean(this.props.selected);
     const timeString = timeValid
       ? `${addZero(time.getHours())}:${addZero(time.getMinutes())}`
       : "";
     if (this.props.showTimeInput) {
       return (
         <InputTime
+          {...this.props}
           date={time}
           timeString={timeString}
-          timeInputLabel={this.props.timeInputLabel}
           onChange={this.props.onTimeChange}
-          customTimeInput={this.props.customTimeInput}
         />
       );
     }
+    return;
   };
 
-  renderAriaLiveRegion = () => {
+  renderAriaLiveRegion = (): JSX.Element => {
     const { startPeriod, endPeriod } = getYearsPeriod(
       this.state.date,
       this.props.yearItemNumber,
@@ -1093,7 +1057,7 @@ export default class Calendar extends React.Component {
     );
   };
 
-  renderChildren = () => {
+  renderChildren = (): JSX.Element | undefined => {
     if (this.props.children) {
       return (
         <div className="react-datepicker__children-container">
@@ -1101,9 +1065,10 @@ export default class Calendar extends React.Component {
         </div>
       );
     }
+    return;
   };
 
-  render() {
+  render(): JSX.Element {
     const Container = this.props.container || CalendarContainer;
     return (
       <div style={{ display: "contents" }} ref={this.containerRef}>
diff --git a/src/calendar_icon.tsx b/src/calendar_icon.tsx
index ed4e4af2..07ca3432 100644
--- a/src/calendar_icon.tsx
+++ b/src/calendar_icon.tsx
@@ -3,7 +3,7 @@ import React from "react";
 interface CalendarIconProps {
   icon: string | React.ReactNode;
   className?: string;
-  onClick?: (e: React.MouseEvent) => void;
+  onClick?: (event: React.MouseEvent) => void;
 }
 
 /**
@@ -46,13 +46,13 @@ const CalendarIcon: React.FC<CalendarIconProps> = ({
     // Because we are checking that typeof icon is string first, we can safely cast icon as React.ReactElement on types level and code level
     return React.cloneElement(icon as React.ReactElement, {
       className: `${icon.props.className || ""} ${defaultClass} ${className}`,
-      onClick: (e: React.MouseEvent) => {
+      onClick: (event: React.MouseEvent) => {
         if (typeof icon.props.onClick === "function") {
-          icon.props.onClick(e);
+          icon.props.onClick(event);
         }
 
         if (typeof onClick === "function") {
-          onClick(e);
+          onClick(event);
         }
       },
     });
diff --git a/src/date_utils.ts b/src/date_utils.ts
index 8d9760ba..bf60ae02 100644
--- a/src/date_utils.ts
+++ b/src/date_utils.ts
@@ -31,6 +31,7 @@ import { setQuarter } from "date-fns/setQuarter";
 import { setYear } from "date-fns/setYear";
 import { min } from "date-fns/min";
 import { max } from "date-fns/max";
+import { set } from "date-fns/set";
 import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
 import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
 import { differenceInCalendarYears } from "date-fns/differenceInCalendarYears";
@@ -108,8 +109,8 @@ export function newDate(value?: string | Date | number | null): Date {
 export function parseDate(
   value: string,
   dateFormat: string | string[],
-  locale?: Locale,
-  strictParsing?: boolean,
+  locale: Locale | undefined,
+  strictParsing: boolean,
   minDate?: Date,
 ): Date | null {
   let parsedDate = null;
@@ -177,7 +178,7 @@ export function parseDate(
 
 // ** Date "Reflection" **
 
-export { isDate };
+export { isDate, set };
 
 /**
  * Checks if a given date is valid and not before the minimum date.
@@ -243,10 +244,8 @@ export function formatDate(
  * @returns - The formatted date or an empty string.
  */
 export function safeDateFormat(
-  date?: Date,
-  { dateFormat, locale }: { dateFormat: string | string[]; locale?: Locale } = {
-    dateFormat: "yyyy-MM-dd",
-  },
+  date: Date | null | undefined,
+  { dateFormat, locale }: { dateFormat: string | string[]; locale?: Locale },
 ): string {
   const formatStr = (
     Array.isArray(dateFormat) && dateFormat.length > 0
@@ -265,9 +264,9 @@ export function safeDateFormat(
  * @returns - The formatted date range or an empty string.
  */
 export function safeDateRangeFormat(
-  startDate?: Date,
-  endDate?: Date,
-  props?: { dateFormat: string | string[]; locale: Locale },
+  startDate: Date | null | undefined,
+  endDate: Date | null | undefined,
+  props: { dateFormat: string | string[]; locale?: Locale },
 ): string {
   if (!startDate) {
     return "";
@@ -288,7 +287,7 @@ export function safeDateRangeFormat(
  */
 export function safeMultipleDatesFormat(
   dates: Date[],
-  props: { dateFormat: string | string[]; locale: Locale },
+  props: { dateFormat: string | string[]; locale?: Locale },
 ): string {
   if (!dates?.length) {
     return "";
@@ -435,6 +434,16 @@ export function getStartOfToday(): Date {
 }
 
 // *** End of ***
+/**
+ * Gets the end of the day for a given date.
+ *
+ * @param date - The date.
+ * @returns - The end of the day.
+ */
+export function getEndOfDay(date: Date): Date {
+  return endOfDay(date);
+}
+
 /**
  * Gets the end of the week for a given date.
  *
@@ -544,7 +553,10 @@ export function isSameDay(date1?: Date | null, date2?: Date | null): boolean {
  * @param date2 - The second date.
  * @returns - True if the dates are equal, false otherwise.
  */
-export function isEqual(date1: Date | null, date2: Date | null): boolean {
+export function isEqual(
+  date1: Date | null | undefined,
+  date2: Date | null | undefined,
+): boolean {
   if (date1 && date2) {
     return dfIsEqual(date1, date2);
   } else {
@@ -727,14 +739,15 @@ export function getQuarterShortInLocale(
 
 // ** Utils for some components **
 
-export interface DayDisabledOptions {
+export interface DateFilterOptions {
   minDate?: Date;
   maxDate?: Date;
-  excludeDates?: { date?: Date; message?: string }[] | Date[];
+  excludeDates?: { date: Date; message?: string }[] | Date[];
   excludeDateIntervals?: { start: Date; end: Date }[];
   includeDates?: Date[];
   includeDateIntervals?: { start: Date; end: Date }[];
   filterDate?: (date: Date) => boolean;
+  yearItemNumber?: number;
 }
 
 /**
@@ -754,7 +767,7 @@ export function isDayDisabled(
     includeDates,
     includeDateIntervals,
     filterDate,
-  }: DayDisabledOptions = {},
+  }: DateFilterOptions = {},
 ): boolean {
   return (
     isOutOfBounds(day, { minDate, maxDate }) ||
@@ -781,11 +794,6 @@ export function isDayDisabled(
   );
 }
 
-interface DayExcludedOptions {
-  excludeDates?: { date?: Date }[] | Date[];
-  excludeDateIntervals?: { start: Date; end: Date }[];
-}
-
 /**
  * Checks if a day is excluded.
  *
@@ -795,7 +803,10 @@ interface DayExcludedOptions {
  */
 export function isDayExcluded(
   day: Date,
-  { excludeDates, excludeDateIntervals }: DayExcludedOptions = {},
+  {
+    excludeDates,
+    excludeDateIntervals,
+  }: Pick<DateFilterOptions, "excludeDates" | "excludeDateIntervals"> = {},
 ): boolean {
   if (excludeDateIntervals && excludeDateIntervals.length > 0) {
     return excludeDateIntervals.some(({ start, end }) =>
@@ -815,14 +826,6 @@ export function isDayExcluded(
   );
 }
 
-interface MonthDisabledOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  excludeDates?: Date[];
-  includeDates?: Date[];
-  filterDate?: (date: Date) => boolean;
-}
-
 export function isMonthDisabled(
   month: Date,
   {
@@ -831,14 +834,22 @@ export function isMonthDisabled(
     excludeDates,
     includeDates,
     filterDate,
-  }: MonthDisabledOptions = {},
+  }: Pick<
+    DateFilterOptions,
+    "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate"
+  > = {},
 ): boolean {
   return (
     isOutOfBounds(month, {
       minDate: minDate ? startOfMonth(minDate) : undefined,
       maxDate: maxDate ? endOfMonth(maxDate) : undefined,
     }) ||
-    excludeDates?.some((excludeDate) => isSameMonth(month, excludeDate)) ||
+    excludeDates?.some((excludeDate) =>
+      isSameMonth(
+        month,
+        excludeDate instanceof Date ? excludeDate : excludeDate.date,
+      ),
+    ) ||
     (includeDates &&
       !includeDates.some((includeDate) => isSameMonth(month, includeDate))) ||
     (filterDate && !filterDate(newDate(month))) ||
@@ -869,14 +880,6 @@ export function isMonthInRange(
   return false;
 }
 
-interface QuarterDisabledOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  excludeDates?: Date[];
-  includeDates?: Date[];
-  filterDate?: (date: Date) => boolean;
-}
-
 export function isQuarterDisabled(
   quarter: Date,
   {
@@ -885,11 +888,19 @@ export function isQuarterDisabled(
     excludeDates,
     includeDates,
     filterDate,
-  }: QuarterDisabledOptions = {},
+  }: Pick<
+    DateFilterOptions,
+    "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate"
+  > = {},
 ): boolean {
   return (
     isOutOfBounds(quarter, { minDate, maxDate }) ||
-    excludeDates?.some((excludeDate) => isSameQuarter(quarter, excludeDate)) ||
+    excludeDates?.some((excludeDate) =>
+      isSameQuarter(
+        quarter,
+        excludeDate instanceof Date ? excludeDate : excludeDate.date,
+      ),
+    ) ||
     (includeDates &&
       !includeDates.some((includeDate) =>
         isSameQuarter(quarter, includeDate),
@@ -899,7 +910,11 @@ export function isQuarterDisabled(
   );
 }
 
-export function isYearInRange(year: number, start?: Date, end?: Date): boolean {
+export function isYearInRange(
+  year: number,
+  start?: Date | null,
+  end?: Date | null,
+): boolean {
   if (!start || !end) return false;
   if (!isValidDate(start) || !isValidDate(end)) return false;
   const startYear = getYear(start);
@@ -908,14 +923,6 @@ export function isYearInRange(year: number, start?: Date, end?: Date): boolean {
   return startYear <= year && endYear >= year;
 }
 
-interface YearDisabledOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  excludeDates?: Date[];
-  includeDates?: Date[];
-  filterDate?: (date: Date) => boolean;
-}
-
 export function isYearDisabled(
   year: number,
   {
@@ -924,7 +931,10 @@ export function isYearDisabled(
     excludeDates,
     includeDates,
     filterDate,
-  }: YearDisabledOptions = {},
+  }: Pick<
+    DateFilterOptions,
+    "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate"
+  > = {},
 ): boolean {
   const date = new Date(year, 0, 1);
   return (
@@ -932,7 +942,12 @@ export function isYearDisabled(
       minDate: minDate ? startOfYear(minDate) : undefined,
       maxDate: maxDate ? endOfYear(maxDate) : undefined,
     }) ||
-    excludeDates?.some((excludeDate) => isSameYear(date, excludeDate)) ||
+    excludeDates?.some((excludeDate) =>
+      isSameYear(
+        date,
+        excludeDate instanceof Date ? excludeDate : excludeDate.date,
+      ),
+    ) ||
     (includeDates &&
       !includeDates.some((includeDate) => isSameYear(date, includeDate))) ||
     (filterDate && !filterDate(newDate(date))) ||
@@ -963,14 +978,9 @@ export function isQuarterInRange(
   return false;
 }
 
-interface BoundsOptions {
-  minDate?: Date;
-  maxDate?: Date;
-}
-
 export function isOutOfBounds(
   day: Date,
-  { minDate, maxDate }: BoundsOptions = {},
+  { minDate, maxDate }: Pick<DateFilterOptions, "minDate" | "maxDate"> = {},
 ): boolean {
   return (
     ((minDate && differenceInCalendarDays(day, minDate) < 0) ||
@@ -988,7 +998,9 @@ export function isTimeInList(time: Date, times: Date[]): boolean {
   );
 }
 
-interface TimeDisabledOptions {
+export interface TimeFilterOptions {
+  minTime?: Date;
+  maxTime?: Date;
   excludeTimes?: Date[];
   includeTimes?: Date[];
   filterTime?: (time: Date) => boolean;
@@ -996,7 +1008,14 @@ interface TimeDisabledOptions {
 
 export function isTimeDisabled(
   time: Date,
-  { excludeTimes, includeTimes, filterTime }: TimeDisabledOptions = {},
+  {
+    excludeTimes,
+    includeTimes,
+    filterTime,
+  }: Pick<
+    TimeFilterOptions,
+    "excludeTimes" | "includeTimes" | "filterTime"
+  > = {},
 ): boolean {
   return (
     (excludeTimes && isTimeInList(time, excludeTimes)) ||
@@ -1006,14 +1025,9 @@ export function isTimeDisabled(
   );
 }
 
-interface TimeDisabledOptions {
-  minTime?: Date;
-  maxTime?: Date;
-}
-
 export function isTimeInDisabledRange(
   time: Date,
-  { minTime, maxTime }: TimeDisabledOptions,
+  { minTime, maxTime }: Pick<TimeFilterOptions, "minTime" | "maxTime">,
 ): boolean {
   if (!minTime || !maxTime) {
     throw new Error("Both minTime and maxTime props required");
@@ -1042,15 +1056,12 @@ export function isTimeInDisabledRange(
   return valid;
 }
 
-interface DisabledOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  includeDates?: Date[];
-}
-
 export function monthDisabledBefore(
   day: Date,
-  { minDate, includeDates }: DisabledOptions = {},
+  {
+    minDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "minDate" | "includeDates"> = {},
 ): boolean {
   const previousMonth = subMonths(day, 1);
   return (
@@ -1066,7 +1077,10 @@ export function monthDisabledBefore(
 
 export function monthDisabledAfter(
   day: Date,
-  { maxDate, includeDates }: DisabledOptions = {},
+  {
+    maxDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "maxDate" | "includeDates"> = {},
 ): boolean {
   const nextMonth = addMonths(day, 1);
   return (
@@ -1081,7 +1095,10 @@ export function monthDisabledAfter(
 
 export function quarterDisabledBefore(
   date: Date,
-  { minDate, includeDates }: DisabledOptions = {},
+  {
+    minDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "minDate" | "includeDates"> = {},
 ): boolean {
   const firstDateOfYear = startOfYear(date);
   const previousQuarter = subQuarters(firstDateOfYear, 1);
@@ -1099,7 +1116,10 @@ export function quarterDisabledBefore(
 
 export function quarterDisabledAfter(
   date: Date,
-  { maxDate, includeDates }: DisabledOptions = {},
+  {
+    maxDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "maxDate" | "includeDates"> = {},
 ): boolean {
   const lastDateOfYear = endOfYear(date);
   const nextQuarter = addQuarters(lastDateOfYear, 1);
@@ -1115,16 +1135,12 @@ export function quarterDisabledAfter(
   );
 }
 
-interface YearDisabledOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  includeDates?: Date[];
-  yearItemNumber?: number;
-}
-
 export function yearDisabledBefore(
   day: Date,
-  { minDate, includeDates }: YearDisabledOptions = {},
+  {
+    minDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "minDate" | "includeDates"> = {},
 ): boolean {
   const previousYear = subYears(day, 1);
   return (
@@ -1143,7 +1159,7 @@ export function yearsDisabledBefore(
   {
     minDate,
     yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER,
-  }: YearDisabledOptions = {},
+  }: Pick<DateFilterOptions, "minDate" | "yearItemNumber"> = {},
 ): boolean {
   const previousYear = getStartOfYear(subYears(day, yearItemNumber));
   const { endPeriod } = getYearsPeriod(previousYear, yearItemNumber);
@@ -1153,7 +1169,10 @@ export function yearsDisabledBefore(
 
 export function yearDisabledAfter(
   day: Date,
-  { maxDate, includeDates }: YearDisabledOptions = {},
+  {
+    maxDate,
+    includeDates,
+  }: Pick<DateFilterOptions, "maxDate" | "includeDates"> = {},
 ): boolean {
   const nextYear = addYears(day, 1);
   return (
@@ -1166,17 +1185,12 @@ export function yearDisabledAfter(
   );
 }
 
-interface YearsDisabledAfterOptions {
-  maxDate?: Date;
-  yearItemNumber?: number;
-}
-
 export function yearsDisabledAfter(
   day: Date,
   {
     maxDate,
     yearItemNumber = DEFAULT_YEAR_ITEM_NUMBER,
-  }: YearsDisabledAfterOptions = {},
+  }: Pick<DateFilterOptions, "maxDate" | "yearItemNumber"> = {},
 ): boolean {
   const nextYear = addYears(day, yearItemNumber);
   const { startPeriod } = getYearsPeriod(nextYear, yearItemNumber);
@@ -1184,16 +1198,10 @@ export function yearsDisabledAfter(
   return (maxDateYear && maxDateYear < startPeriod) || false;
 }
 
-interface EffectiveDateOptions {
-  minDate?: Date;
-  maxDate?: Date;
-  includeDates?: Date[];
-}
-
 export function getEffectiveMinDate({
   minDate,
   includeDates,
-}: EffectiveDateOptions): Date | undefined {
+}: Pick<DateFilterOptions, "minDate" | "includeDates">): Date | undefined {
   if (includeDates && minDate) {
     let minDates = includeDates.filter(
       (includeDate) => differenceInCalendarDays(includeDate, minDate) >= 0,
@@ -1209,7 +1217,7 @@ export function getEffectiveMinDate({
 export function getEffectiveMaxDate({
   maxDate,
   includeDates,
-}: EffectiveDateOptions): Date | undefined {
+}: Pick<DateFilterOptions, "maxDate" | "includeDates">): Date | undefined {
   if (includeDates && maxDate) {
     let maxDates = includeDates.filter(
       (includeDate) => differenceInCalendarDays(includeDate, maxDate) <= 0,
@@ -1222,7 +1230,7 @@ export function getEffectiveMaxDate({
   }
 }
 
-interface HighlightDate {
+export interface HighlightDate {
   [className: string]: Date[];
 }
 
@@ -1282,7 +1290,7 @@ export function arraysAreEqual<T>(array1: T[], array2: T[]): boolean {
   return array1.every((value, index) => value === array2[index]);
 }
 
-interface Holiday {
+export interface HolidayItem {
   date: Date;
   holidayName: string;
 }
@@ -1301,7 +1309,7 @@ export type HolidaysMap = Map<string, ClassNamesObj>;
  * @returns Map containing date as key and array of className and holiday name as value
  */
 export function getHolidaysMap(
-  holidayDates: Holiday[] = [],
+  holidayDates: HolidayItem[] = [],
   defaultClassName: string = "react-datepicker__day--holidays",
 ): HolidaysMap {
   const dateClasses = new Map<string, ClassNamesObj>();
diff --git a/src/day.tsx b/src/day.tsx
index 4416fc02..9685b6a6 100644
--- a/src/day.tsx
+++ b/src/day.tsx
@@ -15,30 +15,40 @@ import {
   getDayOfWeekCode,
   getStartOfWeek,
   formatDate,
-  type DayDisabledOptions,
+  type DateFilterOptions,
   type DateNumberType,
   type Locale,
   type HolidaysMap,
 } from "./date_utils";
 
-interface DayProps {
+interface DayProps
+  extends Pick<
+    DateFilterOptions,
+    | "minDate"
+    | "maxDate"
+    | "excludeDates"
+    | "excludeDateIntervals"
+    | "includeDateIntervals"
+    | "includeDates"
+    | "filterDate"
+  > {
   ariaLabelPrefixWhenEnabled?: string;
   ariaLabelPrefixWhenDisabled?: string;
   disabledKeyboardNavigation?: boolean;
   day: Date;
   dayClassName?: (date: Date) => string;
   endDate?: Date;
-  highlightDates?: Map<string, Date>;
+  highlightDates?: Map<string, string[]>;
   holidays?: HolidaysMap;
   inline?: boolean;
   shouldFocusDayInline?: boolean;
-  month?: number;
+  month: number;
   onClick?: React.MouseEventHandler<HTMLDivElement>;
   onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
   handleOnKeyDown: React.KeyboardEventHandler<HTMLDivElement>;
   usePointerEvent?: boolean;
-  preSelection?: Date;
-  selected?: Date;
+  preSelection?: Date | null;
+  selected?: Date | null;
   selectingDate?: Date;
   selectsEnd?: boolean;
   selectsStart?: boolean;
@@ -51,17 +61,10 @@ interface DayProps {
   startDate?: Date;
   renderDayContents?: (day: number, date: Date) => React.ReactNode;
   containerRef?: React.RefObject<HTMLDivElement>;
-  excludeDates?: DayDisabledOptions["excludeDates"];
-  excludeDateIntervals?: DayDisabledOptions["excludeDateIntervals"];
   calendarStartDay?: DateNumberType;
   locale?: Locale;
   monthShowsDuplicateDaysEnd?: boolean;
   monthShowsDuplicateDaysStart?: boolean;
-  includeDates?: DayDisabledOptions["includeDates"];
-  includeDateIntervals?: DayDisabledOptions["includeDateIntervals"];
-  minDate?: Date;
-  maxDate?: Date;
-  filterDate?: (date: Date) => boolean;
 }
 
 /**
@@ -171,7 +174,8 @@ export default class Day extends Component<DayProps> {
     this.props.handleOnKeyDown(event);
   };
 
-  isSameDay = (other?: Date) => isSameDay(this.props.day, other);
+  isSameDay = (other: Date | null | undefined) =>
+    isSameDay(this.props.day, other);
 
   isKeyboardSelected = () => {
     if (this.props.disabledKeyboardNavigation) {
@@ -216,7 +220,7 @@ export default class Day extends Component<DayProps> {
       ),
     );
 
-  isSameWeek = (other?: Date) =>
+  isSameWeek = (other?: Date | null) =>
     this.props.showWeekPicker &&
     isSameDay(
       other,
@@ -227,7 +231,7 @@ export default class Day extends Component<DayProps> {
       ),
     );
 
-  isSameDayOrWeek = (other?: Date) =>
+  isSameDayOrWeek = (other?: Date | null) =>
     this.isSameDay(other) || this.isSameWeek(other);
 
   getHighLightedClass = () => {
diff --git a/src/index.jsx b/src/index.tsx
similarity index 61%
rename from src/index.jsx
rename to src/index.tsx
index 15a9726b..7b98a00e 100644
--- a/src/index.jsx
+++ b/src/index.tsx
@@ -1,22 +1,18 @@
-import React from "react";
-import PropTypes from "prop-types";
+import React, { Component, cloneElement } from "react";
 import Calendar from "./calendar";
 import CalendarIcon from "./calendar_icon";
 import Portal from "./portal";
 import PopperComponent from "./popper_component";
-import { popperPlacementPositions } from "./with_floating";
 import { clsx } from "clsx";
-import { set } from "date-fns/set";
-import { startOfDay } from "date-fns/startOfDay";
-import { endOfDay } from "date-fns/endOfDay";
-import { isValid } from "date-fns/isValid";
 import {
+  set,
   newDate,
   isDate,
   isBefore,
   isAfter,
   isEqual,
   setTime,
+  isValid,
   getSeconds,
   getMinutes,
   getHours,
@@ -50,9 +46,17 @@ import {
   safeMultipleDatesFormat,
   getHolidaysMap,
   isDateBefore,
+  getStartOfDay,
+  getEndOfDay,
+  type HighlightDate,
+  type HolidayItem,
 } from "./date_utils";
 import TabLoop from "./tab_loop";
-import onClickOutside from "react-onclickoutside";
+import onClickOutside, {
+  // eslint-disable-next-line unused-imports/no-unused-imports
+  type WrapperInstance,
+  type AdditionalProps,
+} from "react-onclickoutside";
 
 export { default as CalendarContainer } from "./calendar_container";
 
@@ -62,7 +66,10 @@ const outsideClickIgnoreClass = "react-datepicker-ignore-onclickoutside";
 const WrappedCalendar = onClickOutside(Calendar);
 
 // Compares dates year+month combinations
-function hasPreSelectionChanged(date1, date2) {
+function hasPreSelectionChanged(
+  date1?: Date | null,
+  date2?: Date | null,
+): boolean {
   if (date1 && date2) {
     return (
       getMonth(date1) !== getMonth(date2) || getYear(date1) !== getYear(date2)
@@ -77,7 +84,172 @@ function hasPreSelectionChanged(date1, date2) {
  */
 const INPUT_ERR_1 = "Date input not valid.";
 
-export default class DatePicker extends React.Component {
+interface Holiday {
+  date: string;
+  holidayName: string;
+}
+
+type CalendarProps = React.ComponentPropsWithoutRef<typeof Calendar>;
+
+interface CalendarIconProps
+  extends React.ComponentPropsWithoutRef<typeof CalendarIcon> {}
+
+interface PortalProps extends React.ComponentPropsWithoutRef<typeof Portal> {}
+
+interface PopperComponentProps
+  extends React.ComponentPropsWithoutRef<typeof PopperComponent> {}
+
+export type DatePickerProps = Omit<
+  CalendarProps,
+  | "setOpen"
+  | "dateFormat"
+  | "preSelection"
+  | "onSelect"
+  | "onClickOutside"
+  | "highlightDates"
+  | "holidays"
+  | "shouldFocusDayInline"
+  | "outsideClickIgnoreClass"
+  | "monthSelectedIn"
+  | "onDropdownFocus"
+  | "onTimeChange"
+  | "className"
+  | "container"
+  | "handleOnKeyDown"
+  | "handleOnDayKeyDown"
+  | "isInputFocused"
+  | "setPreSelection"
+  | "selectsRange"
+  | "selectsMultiple"
+> &
+  Pick<AdditionalProps, "excludeScrollbar"> &
+  Pick<CalendarIconProps, "icon"> &
+  Omit<PortalProps, "children"> &
+  Omit<
+    PopperComponentProps,
+    | "className"
+    | "hidePopper"
+    | "targetComponent"
+    | "popperComponent"
+    | "popperOnKeyDown"
+    | "showArrow"
+  > & {
+    dateFormatCalendar: CalendarProps["dateFormat"];
+    calendarClassName?: CalendarProps["className"];
+    calendarContainer?: CalendarProps["container"];
+    onKeyDown?: (event: React.KeyboardEvent<HTMLElement>) => void;
+    popperClassName?: PopperComponentProps["className"];
+    showPopperArrow?: PopperComponentProps["showArrow"];
+    open?: boolean;
+    disabled?: boolean;
+    readOnly?: boolean;
+    startOpen?: boolean;
+    onFocus?: React.FocusEventHandler<HTMLElement>;
+    onBlur?: React.FocusEventHandler<HTMLElement>;
+    onClickOutside?: React.MouseEventHandler<HTMLElement>;
+    onInputClick?: VoidFunction;
+    preventOpenOnFocus?: boolean;
+    closeOnScroll?: boolean | ((event: Event) => boolean);
+    isClearable?: boolean;
+    clearButtonTitle?: string;
+    clearButtonClassName?: string;
+    ariaLabelClose?: string;
+    className?: string;
+    customInput?: Parameters<typeof cloneElement>[0];
+    dateFormat?: string | string[];
+    showDateSelect?: boolean;
+    highlightDates?: (Date | HighlightDate)[];
+    onCalendarOpen?: VoidFunction;
+    onCalendarClose?: VoidFunction;
+    strictParsing?: boolean;
+    swapRange?: boolean;
+    onInputError?: (error: { code: 1; msg: string }) => void;
+    allowSameDay?: boolean;
+    withPortal?: boolean;
+    focusSelectedMonth?: boolean;
+    showIcon?: boolean;
+    calendarIconClassname?: never;
+    calendarIconClassName?: string;
+    toggleCalendarOnIconClick?: boolean;
+    holidays?: Holiday[];
+    startDate?: Date;
+    selected?: Date | null;
+    value?: string;
+    customInputRef?: string;
+    id?: string;
+    name?: string;
+    form?: string;
+    autoFocus?: boolean;
+    placeholderText?: string;
+    autoComplete?: string;
+    title?: string;
+    required?: string;
+    tabIndex?: string;
+    ariaDescribedBy?: string;
+    ariaInvalid?: string;
+    ariaLabelledBy?: string;
+    ariaRequired?: string;
+    onChangeRaw?: (
+      event?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
+    ) => void;
+    onSelect: (
+      date: Date | null,
+      event?:
+        | React.MouseEvent<HTMLElement, MouseEvent>
+        | React.KeyboardEvent<HTMLElement>,
+    ) => void;
+  } & (
+    | {
+        selectsRange?: never;
+        selectsMultiple?: never;
+        onChange: (
+          date: Date | null,
+          event?:
+            | React.MouseEvent<HTMLElement>
+            | React.KeyboardEvent<HTMLElement>,
+        ) => void;
+      }
+    | {
+        selectsRange: true;
+        selectsMultiple?: never;
+        onChange: (
+          date: [Date | null, Date | null],
+          event?:
+            | React.MouseEvent<HTMLElement>
+            | React.KeyboardEvent<HTMLElement>,
+        ) => void;
+      }
+    | {
+        selectsRange?: never;
+        selectsMultiple: true;
+        onChange: (
+          date: Date[] | null,
+          event?:
+            | React.MouseEvent<HTMLElement>
+            | React.KeyboardEvent<HTMLElement>,
+        ) => void;
+      }
+  );
+
+interface DatePickerState {
+  open: boolean;
+  lastPreSelectChange?:
+    | typeof PRESELECT_CHANGE_VIA_INPUT
+    | typeof PRESELECT_CHANGE_VIA_NAVIGATE;
+  inputValue: string | null;
+  preventFocus: boolean;
+  preSelection?: CalendarProps["preSelection"];
+  shouldFocusDayInline?: CalendarProps["shouldFocusDayInline"];
+  monthSelectedIn?: CalendarProps["monthSelectedIn"];
+  focused?: CalendarProps["isInputFocused"];
+  highlightDates: Required<CalendarProps>["highlightDates"];
+  isRenderAriaLiveMessage?: boolean;
+}
+
+export default class DatePicker extends Component<
+  DatePickerProps,
+  DatePickerState
+> {
   static get defaultProps() {
     return {
       allowSameDay: false,
@@ -139,208 +311,20 @@ export default class DatePicker extends React.Component {
     };
   }
 
-  static propTypes = {
-    adjustDateOnChange: PropTypes.bool,
-    allowSameDay: PropTypes.bool,
-    ariaDescribedBy: PropTypes.string,
-    ariaInvalid: PropTypes.string,
-    ariaLabelClose: PropTypes.string,
-    ariaLabelledBy: PropTypes.string,
-    ariaRequired: PropTypes.string,
-    autoComplete: PropTypes.string,
-    autoFocus: PropTypes.bool,
-    calendarClassName: PropTypes.string,
-    calendarContainer: PropTypes.func,
-    children: PropTypes.node,
-    chooseDayAriaLabelPrefix: PropTypes.string,
-    closeOnScroll: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
-    className: PropTypes.string,
-    customInput: PropTypes.element,
-    customInputRef: PropTypes.string,
-    calendarStartDay: PropTypes.number,
-    // eslint-disable-next-line react/no-unused-prop-types
-    dateFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
-    dateFormatCalendar: PropTypes.string,
-    dayClassName: PropTypes.func,
-    weekDayClassName: PropTypes.func,
-    disabledDayAriaLabelPrefix: PropTypes.string,
-    monthClassName: PropTypes.func,
-    timeClassName: PropTypes.func,
-    disabled: PropTypes.bool,
-    disabledKeyboardNavigation: PropTypes.bool,
-    dropdownMode: PropTypes.oneOf(["scroll", "select"]).isRequired,
-    endDate: PropTypes.instanceOf(Date),
-    excludeDates: PropTypes.arrayOf(
-      PropTypes.oneOfType([
-        PropTypes.instanceOf(Date),
-        PropTypes.shape({
-          date: PropTypes.instanceOf(Date).isRequired,
-          message: PropTypes.string,
-        }),
-      ]),
-    ),
-    excludeDateIntervals: PropTypes.arrayOf(
-      PropTypes.shape({
-        start: PropTypes.instanceOf(Date),
-        end: PropTypes.instanceOf(Date),
-      }),
-    ),
-    filterDate: PropTypes.func,
-    fixedHeight: PropTypes.bool,
-    form: PropTypes.string,
-    formatWeekNumber: PropTypes.func,
-    highlightDates: PropTypes.array,
-    holidays: PropTypes.array,
-    id: PropTypes.string,
-    includeDates: PropTypes.array,
-    includeDateIntervals: PropTypes.array,
-    includeTimes: PropTypes.array,
-    injectTimes: PropTypes.array,
-    inline: PropTypes.bool,
-    isClearable: PropTypes.bool,
-    toggleCalendarOnIconClick: PropTypes.oneOfType([
-      PropTypes.func,
-      PropTypes.bool,
-    ]),
-    showIcon: PropTypes.bool,
-    icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
-    calendarIconClassname: PropTypes.string,
-    calendarIconClassName: PropTypes.string,
-    locale: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.shape({ locale: PropTypes.object }),
-    ]),
-    maxDate: PropTypes.instanceOf(Date),
-    minDate: PropTypes.instanceOf(Date),
-    monthsShown: PropTypes.number,
-    name: PropTypes.string,
-    onBlur: PropTypes.func,
-    onChange: PropTypes.func.isRequired,
-    onSelect: PropTypes.func,
-    onWeekSelect: PropTypes.func,
-    onClickOutside: PropTypes.func,
-    onChangeRaw: PropTypes.func,
-    onFocus: PropTypes.func,
-    onInputClick: PropTypes.func,
-    onKeyDown: PropTypes.func,
-    onMonthChange: PropTypes.func,
-    onYearChange: PropTypes.func,
-    onInputError: PropTypes.func,
-    open: PropTypes.bool,
-    onCalendarOpen: PropTypes.func,
-    onCalendarClose: PropTypes.func,
-    openToDate: PropTypes.instanceOf(Date),
-    peekNextMonth: PropTypes.bool,
-    placeholderText: PropTypes.string,
-    popperContainer: PropTypes.func,
-    popperClassName: PropTypes.string, // <PopperComponent/> props
-    popperModifiers: PropTypes.arrayOf(PropTypes.object), // <PopperComponent/> props
-    popperPlacement: PropTypes.oneOf(popperPlacementPositions), // <PopperComponent/> props
-    popperProps: PropTypes.object,
-    preventOpenOnFocus: PropTypes.bool,
-    readOnly: PropTypes.bool,
-    required: PropTypes.bool,
-    scrollableYearDropdown: PropTypes.bool,
-    scrollableMonthYearDropdown: PropTypes.bool,
-    selected: PropTypes.instanceOf(Date),
-    selectsEnd: PropTypes.bool,
-    selectsStart: PropTypes.bool,
-    selectsRange: PropTypes.bool,
-    selectsDisabledDaysInRange: PropTypes.bool,
-    selectsMultiple: PropTypes.bool,
-    selectedDates: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
-    showMonthDropdown: PropTypes.bool,
-    showPreviousMonths: PropTypes.bool,
-    showMonthYearDropdown: PropTypes.bool,
-    showWeekNumbers: PropTypes.bool,
-    showYearDropdown: PropTypes.bool,
-    strictParsing: PropTypes.bool,
-    swapRange: PropTypes.bool,
-    forceShowMonthNavigation: PropTypes.bool,
-    showDisabledMonthNavigation: PropTypes.bool,
-    startDate: PropTypes.instanceOf(Date),
-    startOpen: PropTypes.bool,
-    tabIndex: PropTypes.number,
-    timeCaption: PropTypes.string,
-    title: PropTypes.string,
-    todayButton: PropTypes.node,
-    useWeekdaysShort: PropTypes.bool,
-    formatWeekDay: PropTypes.func,
-    value: PropTypes.string,
-    weekLabel: PropTypes.string,
-    withPortal: PropTypes.bool,
-    portalId: PropTypes.string,
-    portalHost: PropTypes.instanceOf(ShadowRoot),
-    yearItemNumber: PropTypes.number,
-    yearDropdownItemNumber: PropTypes.number,
-    shouldCloseOnSelect: PropTypes.bool,
-    showTimeInput: PropTypes.bool,
-    showMonthYearPicker: PropTypes.bool,
-    showFullMonthYearPicker: PropTypes.bool,
-    showTwoColumnMonthYearPicker: PropTypes.bool,
-    showFourColumnMonthYearPicker: PropTypes.bool,
-    showYearPicker: PropTypes.bool,
-    showQuarterYearPicker: PropTypes.bool,
-    showWeekPicker: PropTypes.bool,
-    showDateSelect: PropTypes.bool,
-    showTimeSelect: PropTypes.bool,
-    showTimeSelectOnly: PropTypes.bool,
-    timeFormat: PropTypes.string,
-    timeIntervals: PropTypes.number,
-    minTime: PropTypes.instanceOf(Date),
-    maxTime: PropTypes.instanceOf(Date),
-    excludeTimes: PropTypes.array,
-    filterTime: PropTypes.func,
-    useShortMonthInDropdown: PropTypes.bool,
-    clearButtonTitle: PropTypes.string,
-    clearButtonClassName: PropTypes.string,
-    previousMonthAriaLabel: PropTypes.string,
-    previousMonthButtonLabel: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.node,
-    ]),
-    nextMonthAriaLabel: PropTypes.string,
-    nextMonthButtonLabel: PropTypes.oneOfType([
-      PropTypes.string,
-      PropTypes.node,
-    ]),
-    previousYearAriaLabel: PropTypes.string,
-    previousYearButtonLabel: PropTypes.string,
-    nextYearAriaLabel: PropTypes.string,
-    nextYearButtonLabel: PropTypes.string,
-    timeInputLabel: PropTypes.string,
-    renderCustomHeader: PropTypes.func,
-    renderDayContents: PropTypes.func,
-    renderMonthContent: PropTypes.func,
-    renderQuarterContent: PropTypes.func,
-    renderYearContent: PropTypes.func,
-    wrapperClassName: PropTypes.string,
-    focusSelectedMonth: PropTypes.bool,
-    onDayMouseEnter: PropTypes.func,
-    onMonthMouseLeave: PropTypes.func,
-    onYearMouseEnter: PropTypes.func,
-    onYearMouseLeave: PropTypes.func,
-    showPopperArrow: PropTypes.bool,
-    excludeScrollbar: PropTypes.bool,
-    enableTabLoop: PropTypes.bool,
-    customTimeInput: PropTypes.element,
-    weekAriaLabelPrefix: PropTypes.string,
-    monthAriaLabelPrefix: PropTypes.string,
-    usePointerEvent: PropTypes.bool,
-    yearClassName: PropTypes.func,
-  };
-
-  constructor(props) {
+  constructor(props: Readonly<DatePickerProps>) {
     super(props);
     this.state = this.calcInitialState();
-    this.preventFocusTimeout = null;
+    this.preventFocusTimeout = undefined;
   }
 
-  componentDidMount() {
+  componentDidMount(): void {
     window.addEventListener("scroll", this.onScroll, true);
   }
 
-  componentDidUpdate(prevProps, prevState) {
+  componentDidUpdate(
+    prevProps: Readonly<DatePickerProps>,
+    prevState: Readonly<DatePickerState>,
+  ): void {
     if (
       prevProps.inline &&
       hasPreSelectionChanged(prevProps.selected, this.props.selected)
@@ -367,21 +351,30 @@ export default class DatePicker extends React.Component {
 
     if (prevState.open !== this.state.open) {
       if (prevState.open === false && this.state.open === true) {
-        this.props.onCalendarOpen();
+        this.props.onCalendarOpen?.();
       }
 
       if (prevState.open === true && this.state.open === false) {
-        this.props.onCalendarClose();
+        this.props.onCalendarClose?.();
       }
     }
   }
 
-  componentWillUnmount() {
+  componentWillUnmount(): void {
     this.clearPreventFocusTimeout();
     window.removeEventListener("scroll", this.onScroll, true);
   }
 
-  getPreSelection = () =>
+  preventFocusTimeout: ReturnType<typeof setTimeout> | undefined;
+
+  inputFocusTimeout: ReturnType<typeof setTimeout> | undefined;
+
+  calendar: WrapperInstance<Readonly<CalendarProps>, typeof Calendar> | null =
+    null;
+
+  input: HTMLElement | null = null;
+
+  getPreSelection = (): Date =>
     this.props.openToDate
       ? this.props.openToDate
       : this.props.selectsEnd && this.props.startDate
@@ -392,7 +385,7 @@ export default class DatePicker extends React.Component {
 
   // Convert the date from string format to standard Date format
   modifyHolidays = () =>
-    this.props.holidays?.reduce((accumulator, holiday) => {
+    this.props.holidays?.reduce<HolidayItem[]>((accumulator, holiday) => {
       const date = new Date(holiday.date);
       if (!isValid(date)) {
         return accumulator;
@@ -401,19 +394,20 @@ export default class DatePicker extends React.Component {
       return [...accumulator, { ...holiday, date }];
     }, []);
 
-  calcInitialState = () => {
+  calcInitialState = (): DatePickerState => {
     const defaultPreSelection = this.getPreSelection();
     const minDate = getEffectiveMinDate(this.props);
     const maxDate = getEffectiveMaxDate(this.props);
     const boundedPreSelection =
-      minDate && isBefore(defaultPreSelection, startOfDay(minDate))
+      minDate && isBefore(defaultPreSelection, getStartOfDay(minDate))
         ? minDate
-        : maxDate && isAfter(defaultPreSelection, endOfDay(maxDate))
+        : maxDate && isAfter(defaultPreSelection, getEndOfDay(maxDate))
           ? maxDate
           : defaultPreSelection;
     return {
       open: this.props.startOpen || false,
       preventFocus: false,
+      inputValue: null,
       preSelection:
         (this.props.selectsRange
           ? this.props.startDate
@@ -449,7 +443,7 @@ export default class DatePicker extends React.Component {
     this.cancelFocusInput();
   };
 
-  setOpen = (open, skipSetBlur = false) => {
+  setOpen = (open: boolean, skipSetBlur: boolean = false): void => {
     this.setState(
       {
         open: open,
@@ -462,7 +456,7 @@ export default class DatePicker extends React.Component {
       () => {
         if (!open) {
           this.setState(
-            (prev) => ({
+            (prev: DatePickerState) => ({
               focused: skipSetBlur ? prev.focused : false,
             }),
             () => {
@@ -475,16 +469,16 @@ export default class DatePicker extends React.Component {
       },
     );
   };
-  inputOk = () => isDate(this.state.preSelection);
+  inputOk = (): boolean => isDate(this.state.preSelection);
 
   isCalendarOpen = () =>
     this.props.open === undefined
       ? this.state.open && !this.props.disabled && !this.props.readOnly
       : this.props.open;
 
-  handleFocus = (event) => {
+  handleFocus = (event: React.FocusEvent<HTMLElement>): void => {
     if (!this.state.preventFocus) {
-      this.props.onFocus(event);
+      this.props.onFocus?.(event);
       if (!this.props.preventOpenOnFocus && !this.props.readOnly) {
         this.setOpen(true);
       }
@@ -492,7 +486,7 @@ export default class DatePicker extends React.Component {
     this.setState({ focused: true });
   };
 
-  sendFocusBackToInput = () => {
+  sendFocusBackToInput = (): void => {
     // Clear previous timeout if it exists
     if (this.preventFocusTimeout) {
       this.clearPreventFocusTimeout();
@@ -501,8 +495,8 @@ export default class DatePicker extends React.Component {
     // close the popper and refocus the input
     // stop the input from auto opening onFocus
     // setFocus to the input
-    this.setState({ preventFocus: true }, () => {
-      this.preventFocusTimeout = setTimeout(() => {
+    this.setState({ preventFocus: true }, (): void => {
+      this.preventFocusTimeout = setTimeout((): void => {
         this.setFocus();
         this.setState({ preventFocus: false });
       });
@@ -511,7 +505,7 @@ export default class DatePicker extends React.Component {
 
   cancelFocusInput = () => {
     clearTimeout(this.inputFocusTimeout);
-    this.inputFocusTimeout = null;
+    this.inputFocusTimeout = undefined;
   };
 
   deferFocusInput = () => {
@@ -523,44 +517,53 @@ export default class DatePicker extends React.Component {
     this.cancelFocusInput();
   };
 
-  handleBlur = (event) => {
+  handleBlur = (event: React.FocusEvent<HTMLElement>) => {
     if (!this.state.open || this.props.withPortal || this.props.showTimeInput) {
-      this.props.onBlur(event);
+      this.props.onBlur?.(event);
     }
 
     this.setState({ focused: false });
   };
 
-  handleCalendarClickOutside = (event) => {
+  handleCalendarClickOutside = (event: React.MouseEvent<HTMLElement>) => {
     if (!this.props.inline) {
       this.setOpen(false);
     }
-    this.props.onClickOutside(event);
+    this.props.onClickOutside?.(event);
     if (this.props.withPortal) {
       event.preventDefault();
     }
   };
 
-  handleChange = (...allArgs) => {
-    let event = allArgs[0];
+  handleChange = (
+    ...allArgs: Parameters<Required<DatePickerProps>["onChangeRaw"]>
+  ) => {
+    const event = allArgs[0];
     if (this.props.onChangeRaw) {
       this.props.onChangeRaw.apply(this, allArgs);
       if (
+        !event ||
         typeof event.isDefaultPrevented !== "function" ||
         event.isDefaultPrevented()
       ) {
         return;
       }
     }
+
     this.setState({
-      inputValue: event.target.value,
+      inputValue:
+        event?.target instanceof HTMLInputElement ? event.target.value : null,
       lastPreSelectChange: PRESELECT_CHANGE_VIA_INPUT,
     });
+    const {
+      dateFormat = DatePicker.defaultProps.dateFormat,
+      strictParsing = DatePicker.defaultProps.strictParsing,
+    } = this.props;
     let date = parseDate(
-      event.target.value,
-      this.props.dateFormat,
+      event?.target instanceof HTMLInputElement ? event.target.value : "",
+      dateFormat,
       this.props.locale,
-      this.props.strictParsing,
+      strictParsing,
       this.props.minDate,
     );
     // Use date from `selected` prop when manipulating only time for input value
@@ -576,12 +579,20 @@ export default class DatePicker extends React.Component {
         seconds: getSeconds(date),
       });
     }
-    if (date || !event.target.value) {
+    if (
+      date ||
+      !(event?.target instanceof HTMLInputElement) ||
+      !event?.target.value
+    ) {
       this.setSelected(date, event, true);
     }
   };
 
-  handleSelect = (date, event, monthSelectedIn) => {
+  handleSelect = (
+    date: Date,
+    event?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
+    monthSelectedIn?: number,
+  ) => {
     if (this.props.shouldCloseOnSelect && !this.props.showTimeSelect) {
       // Preventing onFocus event to fix issue
       // https://github.com/Hacker0x01/react-datepicker/issues/628
@@ -613,7 +624,12 @@ export default class DatePicker extends React.Component {
     }
   };
 
-  setSelected = (date, event, keepInput, monthSelectedIn) => {
+  setSelected = (
+    date: Date | null,
+    event?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
+    keepInput?: boolean,
+    monthSelectedIn?: number,
+  ) => {
     let changedDate = date;
 
     if (this.props.showYearPicker) {
@@ -711,21 +727,23 @@ export default class DatePicker extends React.Component {
           onChange([changedDate, null], event);
         }
       } else if (selectsMultiple) {
-        if (!selectedDates?.length) {
-          onChange([changedDate], event);
-        } else {
-          const isChangedDateAlreadySelected = selectedDates.some(
-            (selectedDate) => isSameDay(selectedDate, changedDate),
-          );
-
-          if (isChangedDateAlreadySelected) {
-            const nextDates = selectedDates.filter(
-              (selectedDate) => !isSameDay(selectedDate, changedDate),
+        if (changedDate !== null) {
+          if (!selectedDates?.length) {
+            onChange([changedDate], event);
+          } else {
+            const isChangedDateAlreadySelected = selectedDates.some(
+              (selectedDate) => isSameDay(selectedDate, changedDate),
             );
 
-            onChange(nextDates, event);
-          } else {
-            onChange([...selectedDates, changedDate], event);
+            if (isChangedDateAlreadySelected) {
+              const nextDates = selectedDates.filter(
+                (selectedDate) => !isSameDay(selectedDate, changedDate),
+              );
+
+              onChange(nextDates, event);
+            } else {
+              onChange([...selectedDates, changedDate], event);
+            }
           }
         }
       } else {
@@ -739,27 +757,27 @@ export default class DatePicker extends React.Component {
     }
   };
 
-  // When checking preSelection via min/maxDate, times need to be manipulated via startOfDay/endOfDay
-  setPreSelection = (date) => {
-    const hasMinDate = typeof this.props.minDate !== "undefined";
-    const hasMaxDate = typeof this.props.maxDate !== "undefined";
+  // When checking preSelection via min/maxDate, times need to be manipulated via getStartOfDay/getEndOfDay
+  setPreSelection = (date?: Date | null): void => {
+    const hasMinDate = this.props.minDate !== undefined;
+    const hasMaxDate = this.props.maxDate !== undefined;
     let isValidDateSelection = true;
     if (date) {
-      const dateStartOfDay = startOfDay(date);
+      const dateStartOfDay = getStartOfDay(date);
       if (hasMinDate && hasMaxDate) {
-        // isDayInRange uses startOfDay internally, so not necessary to manipulate times here
+        // isDayInRange uses getStartOfDay internally, so not necessary to manipulate times here
         isValidDateSelection = isDayInRange(
           date,
           this.props.minDate,
           this.props.maxDate,
         );
       } else if (hasMinDate) {
-        const minDateStartOfDay = startOfDay(this.props.minDate);
+        const minDateStartOfDay = getStartOfDay(this.props.minDate);
         isValidDateSelection =
           isAfter(date, minDateStartOfDay) ||
           isEqual(dateStartOfDay, minDateStartOfDay);
       } else if (hasMaxDate) {
-        const maxDateEndOfDay = endOfDay(this.props.maxDate);
+        const maxDateEndOfDay = getEndOfDay(this.props.maxDate);
         isValidDateSelection =
           isBefore(date, maxDateEndOfDay) ||
           isEqual(dateStartOfDay, maxDateEndOfDay);
@@ -772,15 +790,19 @@ export default class DatePicker extends React.Component {
     }
   };
 
-  toggleCalendar = () => {
+  toggleCalendar = (): void => {
     this.setOpen(!this.state.open);
   };
 
-  handleTimeChange = (time) => {
+  handleTimeChange = (time: Date): void => {
+    if (this.props.selectsRange || this.props.selectsMultiple) {
+      return;
+    }
+
     const selected = this.props.selected
       ? this.props.selected
       : this.getPreSelection();
-    let changedDate = this.props.selected
+    const changedDate = this.props.selected
       ? time
       : setTime(selected, {
           hour: getHours(time),
@@ -805,16 +827,16 @@ export default class DatePicker extends React.Component {
     this.setState({ inputValue: null });
   };
 
-  onInputClick = () => {
+  onInputClick = (): void => {
     if (!this.props.disabled && !this.props.readOnly) {
       this.setOpen(true);
     }
 
-    this.props.onInputClick();
+    this.props.onInputClick?.();
   };
 
-  onInputKeyDown = (event) => {
-    this.props.onKeyDown(event);
+  onInputKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {
+    this.props.onKeyDown?.(event);
     const eventKey = event.key;
 
     if (
@@ -845,9 +867,10 @@ export default class DatePicker extends React.Component {
               ? '.react-datepicker__month-text[tabindex="0"]'
               : '.react-datepicker__day[tabindex="0"]';
         const selectedItem =
-          this.calendar.componentNode &&
+          this.calendar?.componentNode instanceof Element &&
           this.calendar.componentNode.querySelector(selectorString);
-        selectedItem && selectedItem.focus({ preventScroll: true });
+        selectedItem instanceof HTMLElement &&
+          selectedItem.focus({ preventScroll: true });
 
         return;
       }
@@ -873,12 +896,12 @@ export default class DatePicker extends React.Component {
       }
 
       if (!this.inputOk()) {
-        this.props.onInputError({ code: 1, msg: INPUT_ERR_1 });
+        this.props.onInputError?.({ code: 1, msg: INPUT_ERR_1 });
       }
     }
   };
 
-  onPortalKeyDown = (event) => {
+  onPortalKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
     const eventKey = event.key;
     if (eventKey === "Escape") {
       event.preventDefault();
@@ -898,8 +921,8 @@ export default class DatePicker extends React.Component {
   };
 
   // keyDown events passed down to day.jsx
-  onDayKeyDown = (event) => {
-    this.props.onKeyDown(event);
+  onDayKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
+    this.props.onKeyDown?.(event);
     const eventKey = event.key;
     const isShiftKeyActive = event.shiftKey;
 
@@ -913,7 +936,7 @@ export default class DatePicker extends React.Component {
 
       this.setOpen(false);
       if (!this.inputOk()) {
-        this.props.onInputError({ code: 1, msg: INPUT_ERR_1 });
+        this.props.onInputError?.({ code: 1, msg: INPUT_ERR_1 });
       }
     } else if (!this.props.disabledKeyboardNavigation) {
       let newSelection;
@@ -994,7 +1017,7 @@ export default class DatePicker extends React.Component {
 
   // handle generic key down events in the popper that do not adjust or select dates
   // ex: while focusing prev and next month buttons
-  onPopperKeyDown = (event) => {
+  onPopperKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
     const eventKey = event.key;
     if (eventKey === "Escape") {
       event.preventDefault();
@@ -1002,7 +1025,7 @@ export default class DatePicker extends React.Component {
     }
   };
 
-  onClearClick = (event) => {
+  onClearClick = (event?: React.MouseEvent<HTMLButtonElement>): void => {
     if (event) {
       if (event.preventDefault) {
         event.preventDefault();
@@ -1011,6 +1034,13 @@ export default class DatePicker extends React.Component {
 
     this.sendFocusBackToInput();
 
+    const { selectsRange, onChange } = this.props;
+    if (selectsRange) {
+      onChange([null, null], event);
+    } else {
+      onChange(null, event);
+    }
+
     if (this.props.selectsRange) {
       this.props.onChange([null, null], event);
     } else {
@@ -1023,7 +1053,7 @@ export default class DatePicker extends React.Component {
     this.onClearClick();
   };
 
-  onScroll = (event) => {
+  onScroll = (event: Event): void => {
     if (
       typeof this.props.closeOnScroll === "boolean" &&
       this.props.closeOnScroll
@@ -1051,123 +1081,21 @@ export default class DatePicker extends React.Component {
         ref={(elem) => {
           this.calendar = elem;
         }}
-        locale={this.props.locale}
-        calendarStartDay={this.props.calendarStartDay}
-        chooseDayAriaLabelPrefix={this.props.chooseDayAriaLabelPrefix}
-        disabledDayAriaLabelPrefix={this.props.disabledDayAriaLabelPrefix}
-        weekAriaLabelPrefix={this.props.weekAriaLabelPrefix}
-        monthAriaLabelPrefix={this.props.monthAriaLabelPrefix}
-        adjustDateOnChange={this.props.adjustDateOnChange}
+        {...this.props}
+        {...this.state}
         setOpen={this.setOpen}
-        shouldCloseOnSelect={this.props.shouldCloseOnSelect}
         dateFormat={this.props.dateFormatCalendar}
-        useWeekdaysShort={this.props.useWeekdaysShort}
-        formatWeekDay={this.props.formatWeekDay}
-        dropdownMode={this.props.dropdownMode}
-        selected={this.props.selected}
-        preSelection={this.state.preSelection}
         onSelect={this.handleSelect}
-        onWeekSelect={this.props.onWeekSelect}
-        openToDate={this.props.openToDate}
-        minDate={this.props.minDate}
-        maxDate={this.props.maxDate}
-        selectsStart={this.props.selectsStart}
-        selectsEnd={this.props.selectsEnd}
-        selectsRange={this.props.selectsRange}
-        selectsMultiple={this.props.selectsMultiple}
-        selectedDates={this.props.selectedDates}
-        startDate={this.props.startDate}
-        endDate={this.props.endDate}
-        excludeDates={this.props.excludeDates}
-        excludeDateIntervals={this.props.excludeDateIntervals}
-        filterDate={this.props.filterDate}
         onClickOutside={this.handleCalendarClickOutside}
-        formatWeekNumber={this.props.formatWeekNumber}
-        highlightDates={this.state.highlightDates}
         holidays={getHolidaysMap(this.modifyHolidays())}
-        includeDates={this.props.includeDates}
-        includeDateIntervals={this.props.includeDateIntervals}
-        includeTimes={this.props.includeTimes}
-        injectTimes={this.props.injectTimes}
-        inline={this.props.inline}
-        shouldFocusDayInline={this.state.shouldFocusDayInline}
-        peekNextMonth={this.props.peekNextMonth}
-        showMonthDropdown={this.props.showMonthDropdown}
-        showPreviousMonths={this.props.showPreviousMonths}
-        useShortMonthInDropdown={this.props.useShortMonthInDropdown}
-        showMonthYearDropdown={this.props.showMonthYearDropdown}
-        showWeekNumbers={this.props.showWeekNumbers}
-        showYearDropdown={this.props.showYearDropdown}
-        withPortal={this.props.withPortal}
-        forceShowMonthNavigation={this.props.forceShowMonthNavigation}
-        showDisabledMonthNavigation={this.props.showDisabledMonthNavigation}
-        scrollableYearDropdown={this.props.scrollableYearDropdown}
-        scrollableMonthYearDropdown={this.props.scrollableMonthYearDropdown}
-        todayButton={this.props.todayButton}
-        weekLabel={this.props.weekLabel}
         outsideClickIgnoreClass={outsideClickIgnoreClass}
-        fixedHeight={this.props.fixedHeight}
-        monthsShown={this.props.monthsShown}
-        monthSelectedIn={this.state.monthSelectedIn}
         onDropdownFocus={this.handleDropdownFocus}
-        onMonthChange={this.props.onMonthChange}
-        onYearChange={this.props.onYearChange}
-        dayClassName={this.props.dayClassName}
-        weekDayClassName={this.props.weekDayClassName}
-        monthClassName={this.props.monthClassName}
-        timeClassName={this.props.timeClassName}
-        showDateSelect={this.props.showDateSelect}
-        showTimeSelect={this.props.showTimeSelect}
-        showTimeSelectOnly={this.props.showTimeSelectOnly}
         onTimeChange={this.handleTimeChange}
-        timeFormat={this.props.timeFormat}
-        timeIntervals={this.props.timeIntervals}
-        minTime={this.props.minTime}
-        maxTime={this.props.maxTime}
-        excludeTimes={this.props.excludeTimes}
-        filterTime={this.props.filterTime}
-        timeCaption={this.props.timeCaption}
         className={this.props.calendarClassName}
         container={this.props.calendarContainer}
-        yearItemNumber={this.props.yearItemNumber}
-        yearDropdownItemNumber={this.props.yearDropdownItemNumber}
-        previousMonthAriaLabel={this.props.previousMonthAriaLabel}
-        previousMonthButtonLabel={this.props.previousMonthButtonLabel}
-        nextMonthAriaLabel={this.props.nextMonthAriaLabel}
-        nextMonthButtonLabel={this.props.nextMonthButtonLabel}
-        previousYearAriaLabel={this.props.previousYearAriaLabel}
-        previousYearButtonLabel={this.props.previousYearButtonLabel}
-        nextYearAriaLabel={this.props.nextYearAriaLabel}
-        nextYearButtonLabel={this.props.nextYearButtonLabel}
-        timeInputLabel={this.props.timeInputLabel}
-        disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
-        renderCustomHeader={this.props.renderCustomHeader}
-        popperProps={this.props.popperProps}
-        renderDayContents={this.props.renderDayContents}
-        renderMonthContent={this.props.renderMonthContent}
-        renderQuarterContent={this.props.renderQuarterContent}
-        renderYearContent={this.props.renderYearContent}
-        onDayMouseEnter={this.props.onDayMouseEnter}
-        onMonthMouseLeave={this.props.onMonthMouseLeave}
-        onYearMouseEnter={this.props.onYearMouseEnter}
-        onYearMouseLeave={this.props.onYearMouseLeave}
-        selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
-        showTimeInput={this.props.showTimeInput}
-        showMonthYearPicker={this.props.showMonthYearPicker}
-        showFullMonthYearPicker={this.props.showFullMonthYearPicker}
-        showTwoColumnMonthYearPicker={this.props.showTwoColumnMonthYearPicker}
-        showFourColumnMonthYearPicker={this.props.showFourColumnMonthYearPicker}
-        showYearPicker={this.props.showYearPicker}
-        showQuarterYearPicker={this.props.showQuarterYearPicker}
-        showWeekPicker={this.props.showWeekPicker}
-        excludeScrollbar={this.props.excludeScrollbar}
         handleOnKeyDown={this.props.onKeyDown}
         handleOnDayKeyDown={this.onDayKeyDown}
-        isInputFocused={this.state.focused}
-        customTimeInput={this.props.customTimeInput}
         setPreSelection={this.setPreSelection}
-        usePointerEvent={this.props.usePointerEvent}
-        yearClassName={this.props.yearClassName}
       >
         {this.props.children}
       </WrappedCalendar>
@@ -1175,7 +1103,8 @@ export default class DatePicker extends React.Component {
   };
 
   renderAriaLiveRegion = () => {
-    const { dateFormat, locale } = this.props;
+    const { dateFormat = DatePicker.defaultProps.dateFormat, locale } =
+      this.props;
     const isContainsTime =
       this.props.showTimeInput || this.props.showTimeSelect;
     const longDateFormat = isContainsTime ? "PPPPp" : "PPPP";
@@ -1250,23 +1179,30 @@ export default class DatePicker extends React.Component {
 
     const customInput = this.props.customInput || <input type="text" />;
     const customInputRef = this.props.customInputRef || "ref";
+    const { dateFormat = DatePicker.defaultProps.dateFormat, locale } =
+      this.props;
     const inputValue =
       typeof this.props.value === "string"
         ? this.props.value
         : typeof this.state.inputValue === "string"
           ? this.state.inputValue
           : this.props.selectsRange
-            ? safeDateRangeFormat(
-                this.props.startDate,
-                this.props.endDate,
-                this.props,
-              )
+            ? safeDateRangeFormat(this.props.startDate, this.props.endDate, {
+                dateFormat,
+                locale,
+              })
             : this.props.selectsMultiple
-              ? safeMultipleDatesFormat(this.props.selectedDates, this.props)
-              : safeDateFormat(this.props.selected, this.props);
-
-    return React.cloneElement(customInput, {
-      [customInputRef]: (input) => {
+              ? safeMultipleDatesFormat(this.props.selectedDates ?? [], {
+                  dateFormat,
+                  locale,
+                })
+              : safeDateFormat(this.props.selected, {
+                  dateFormat,
+                  locale,
+                });
+
+    return cloneElement(customInput, {
+      [customInputRef]: (input: HTMLElement | null) => {
         this.input = input;
       },
       value: inputValue,
@@ -1294,7 +1230,7 @@ export default class DatePicker extends React.Component {
     });
   };
 
-  renderClearButton = () => {
+  renderClearButton = (): JSX.Element | null => {
     const {
       isClearable,
       disabled,
@@ -1333,7 +1269,7 @@ export default class DatePicker extends React.Component {
     }
   };
 
-  renderInputContainer() {
+  renderInputContainer(): JSX.Element {
     const {
       showIcon,
       icon,
@@ -1377,7 +1313,7 @@ export default class DatePicker extends React.Component {
     );
   }
 
-  render() {
+  render(): JSX.Element | null {
     const calendar = this.renderCalendar();
 
     if (this.props.inline) return calendar;
@@ -1396,14 +1332,7 @@ export default class DatePicker extends React.Component {
       ) : null;
 
       if (this.state.open && this.props.portalId) {
-        portalContainer = (
-          <Portal
-            portalId={this.props.portalId}
-            portalHost={this.props.portalHost}
-          >
-            {portalContainer}
-          </Portal>
-        );
+        portalContainer = <Portal {...this.props}>{portalContainer}</Portal>;
       }
 
       return (
@@ -1416,19 +1345,12 @@ export default class DatePicker extends React.Component {
 
     return (
       <PopperComponent
+        {...this.props}
         className={this.props.popperClassName}
-        wrapperClassName={this.props.wrapperClassName}
         hidePopper={!this.isCalendarOpen()}
-        portalId={this.props.portalId}
-        portalHost={this.props.portalHost}
-        popperModifiers={this.props.popperModifiers}
         targetComponent={this.renderInputContainer()}
-        popperContainer={this.props.popperContainer}
         popperComponent={calendar}
-        popperPlacement={this.props.popperPlacement}
-        popperProps={this.props.popperProps}
         popperOnKeyDown={this.onPopperKeyDown}
-        enableTabLoop={this.props.enableTabLoop}
         showArrow={this.props.showPopperArrow}
       />
     );
diff --git a/src/input_time.tsx b/src/input_time.tsx
index 482718da..d039d2ce 100644
--- a/src/input_time.tsx
+++ b/src/input_time.tsx
@@ -90,8 +90,8 @@ export default class InputTime extends Component<
         name="time-input"
         required
         value={time}
-        onChange={(ev) => {
-          this.onTimeChange(ev.target.value || timeString);
+        onChange={(event) => {
+          this.onTimeChange(event.target.value || timeString);
         }}
       />
     );
diff --git a/src/month.tsx b/src/month.tsx
index 90b19ccc..cefb825d 100644
--- a/src/month.tsx
+++ b/src/month.tsx
@@ -31,12 +31,6 @@ import {
   subMonths,
   subQuarters,
 } from "./date_utils";
-import type {
-  Locale,
-  DayDisabledOptions,
-  DateNumberType,
-  HolidaysMap,
-} from "./date_utils";
 
 const FIXED_HEIGHT_STANDARD_WEEK_COUNT = 6;
 
@@ -90,11 +84,21 @@ function getMonthColumnsLayout(
   return MONTH_COLUMNS_LAYOUT.THREE_COLUMNS;
 }
 
-interface MonthProps {
-  dayClassName?: (date: Date) => string;
+interface WeekProps extends React.ComponentPropsWithoutRef<typeof Week> {}
+
+interface MonthProps
+  extends Omit<
+    WeekProps,
+    | "ariaLabelPrefix"
+    | "day"
+    | "month"
+    | "onDayClick"
+    | "onDayMouseEnter"
+    | "preSelection"
+    | "selected"
+    | "showWeekNumber"
+  > {
   monthClassName?: (date: Date) => string;
-  filterDate?: (date: Date) => boolean;
-  formatWeekNumber?: (date: Date) => number;
   onDayClick?: (
     date: Date,
     event:
@@ -103,11 +107,8 @@ interface MonthProps {
     orderInDisplay?: number,
   ) => void;
   onDayMouseEnter?: (date: Date) => void;
-  onMouseLeave?: () => void;
-  onWeekSelect?: (date: Date) => void;
-  setPreSelection?: (date?: Date) => void;
-  setOpen?: (open: boolean) => void;
-  renderDayContents?: (day: number, date: Date) => React.ReactNode;
+  onMouseLeave?: VoidFunction;
+  setPreSelection?: (date?: Date | null) => void;
   renderMonthContent?: (
     m: number,
     shortMonthText: string,
@@ -115,53 +116,21 @@ interface MonthProps {
     day: Date,
   ) => React.ReactNode;
   renderQuarterContent?: (q: number, shortQuarter: string) => React.ReactNode;
-  handleOnKeyDown?: (event: React.KeyboardEvent) => void;
-  handleOnMonthKeyDown?: (event: React.KeyboardEvent) => void;
+  handleOnMonthKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
   ariaLabelPrefix?: string;
-  chooseDayAriaLabelPrefix?: string;
-  disabledDayAriaLabelPrefix?: string;
-  disabledKeyboardNavigation?: boolean;
   day: Date;
-  endDate?: Date;
   orderInDisplay?: number;
-  excludeDates?: DayDisabledOptions["excludeDates"];
-  excludeDateIntervals?: DayDisabledOptions["excludeDateIntervals"];
   fixedHeight?: boolean;
-  highlightDates?: Map<string, Date>;
-  holidays?: HolidaysMap;
-  includeDates?: DayDisabledOptions["includeDates"];
-  includeDateIntervals?: DayDisabledOptions["includeDateIntervals"];
-  inline?: boolean;
-  shouldFocusDayInline?: boolean;
-  locale?: Locale;
-  maxDate?: Date;
-  minDate?: Date;
-  usePointerEvent?: boolean;
   peekNextMonth?: boolean;
-  preSelection?: Date;
-  selected?: Date;
-  selectingDate?: Date;
-  calendarStartDay?: DateNumberType;
-  selectsEnd?: boolean;
-  selectsStart?: boolean;
-  selectsRange?: boolean;
-  selectsDisabledDaysInRange?: boolean;
-  selectsMultiple?: boolean;
-  selectedDates?: Date[];
-  showWeekNumbers?: boolean;
-  startDate?: Date;
-  shouldCloseOnSelect?: boolean;
+  preSelection?: Date | null;
+  selected?: Date | null;
+  showWeekNumbers?: WeekProps["showWeekNumber"];
   showMonthYearPicker?: boolean;
   showFullMonthYearPicker?: boolean;
   showTwoColumnMonthYearPicker?: boolean;
   showFourColumnMonthYearPicker?: boolean;
   showQuarterYearPicker?: boolean;
-  showWeekPicker?: boolean;
-  isInputFocused?: boolean;
-  weekAriaLabelPrefix?: string;
-  containerRef?: React.RefObject<HTMLDivElement>;
-  monthShowsDuplicateDaysEnd?: boolean;
-  monthShowsDuplicateDaysStart?: boolean;
+  weekAriaLabelPrefix?: WeekProps["ariaLabelPrefix"];
 }
 
 /**
@@ -427,7 +396,7 @@ export default class Month extends Component<MonthProps> {
   isSelectedMonth = (day: Date, m: number, selected: Date) =>
     getMonth(selected) === m && getYear(day) === getYear(selected);
 
-  isSelectedQuarter = (day: Date, q: number, selected: Date) =>
+  isSelectedQuarter = (day: Date, q: number, selected: Date): boolean =>
     getQuarter(day) === q && getYear(day) === getYear(selected);
 
   renderWeeks = () => {
@@ -471,53 +440,16 @@ export default class Month extends Component<MonthProps> {
     while (true) {
       weeks.push(
         <Week
+          {...this.props}
           ariaLabelPrefix={this.props.weekAriaLabelPrefix}
-          chooseDayAriaLabelPrefix={this.props.chooseDayAriaLabelPrefix}
-          disabledDayAriaLabelPrefix={this.props.disabledDayAriaLabelPrefix}
           key={i}
           day={currentWeekStart}
           month={getMonth(this.props.day)}
           onDayClick={this.handleDayClick}
-          usePointerEvent={this.props.usePointerEvent}
           onDayMouseEnter={this.handleDayMouseEnter}
-          onWeekSelect={this.props.onWeekSelect}
-          formatWeekNumber={this.props.formatWeekNumber}
-          locale={this.props.locale}
-          minDate={this.props.minDate}
-          maxDate={this.props.maxDate}
-          excludeDates={this.props.excludeDates}
-          excludeDateIntervals={this.props.excludeDateIntervals}
-          includeDates={this.props.includeDates}
-          includeDateIntervals={this.props.includeDateIntervals}
-          inline={this.props.inline}
-          shouldFocusDayInline={this.props.shouldFocusDayInline}
-          highlightDates={this.props.highlightDates}
-          holidays={this.props.holidays}
-          selectingDate={this.props.selectingDate}
-          filterDate={this.props.filterDate}
-          preSelection={preSelection}
           selected={selected}
-          selectsStart={this.props.selectsStart}
-          selectsEnd={this.props.selectsEnd}
-          selectsRange={this.props.selectsRange}
-          selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
-          selectsMultiple={this.props.selectsMultiple}
-          selectedDates={this.props.selectedDates}
+          preSelection={preSelection}
           showWeekNumber={this.props.showWeekNumbers}
-          showWeekPicker={this.props.showWeekPicker}
-          startDate={this.props.startDate}
-          endDate={this.props.endDate}
-          dayClassName={this.props.dayClassName}
-          setOpen={this.props.setOpen}
-          shouldCloseOnSelect={this.props.shouldCloseOnSelect}
-          disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
-          renderDayContents={this.props.renderDayContents}
-          handleOnKeyDown={this.props.handleOnKeyDown}
-          isInputFocused={this.props.isInputFocused}
-          containerRef={this.props.containerRef}
-          calendarStartDay={this.props.calendarStartDay}
-          monthShowsDuplicateDaysEnd={this.props.monthShowsDuplicateDaysEnd}
-          monthShowsDuplicateDaysStart={this.props.monthShowsDuplicateDaysStart}
         />,
       );
 
@@ -546,7 +478,7 @@ export default class Month extends Component<MonthProps> {
   };
 
   onMonthClick = (
-    e:
+    event:
       | React.MouseEvent<HTMLDivElement, MouseEvent>
       | React.KeyboardEvent<HTMLDivElement>,
     m: number,
@@ -557,7 +489,7 @@ export default class Month extends Component<MonthProps> {
       return;
     }
 
-    this.handleDayClick(getStartOfMonth(labelDate), e);
+    this.handleDayClick(getStartOfMonth(labelDate), event);
   };
 
   onMonthMouseEnter = (m: number) => {
@@ -664,46 +596,25 @@ export default class Month extends Component<MonthProps> {
     handleOnMonthKeyDown && handleOnMonthKeyDown(event);
   };
 
-  private propsToOnDisableClick = (
-    prps: MonthProps,
-  ): {
-    minDate?: Date;
-    maxDate?: Date;
-    excludeDates?: Date[];
-    includeDates?: Date[];
-    filterDate?: (date: Date) => boolean;
-  } => ({
-    minDate: prps.minDate,
-    maxDate: prps.maxDate,
-    excludeDates: prps.excludeDates?.reduce((acc, itm) => {
-      if (itm instanceof Date) {
-        acc.push(itm);
-      } else if (itm.date) {
-        acc.push(itm.date);
-      }
-      return acc;
-    }, [] as Date[]),
-    includeDates: prps.includeDates,
-    filterDate: prps.filterDate,
-  });
-
   onQuarterClick = (
-    e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
+    event:
+      | React.MouseEvent<HTMLDivElement>
+      | React.KeyboardEvent<HTMLDivElement>,
     q: number,
   ) => {
     const labelDate = setQuarter(this.props.day, q);
 
-    if (isQuarterDisabled(labelDate, this.propsToOnDisableClick(this.props))) {
+    if (isQuarterDisabled(labelDate, this.props)) {
       return;
     }
 
-    this.handleDayClick(getStartOfQuarter(labelDate), e);
+    this.handleDayClick(getStartOfQuarter(labelDate), event);
   };
 
   onQuarterMouseEnter = (q: number) => {
     const labelDate = setQuarter(this.props.day, q);
 
-    if (isQuarterDisabled(labelDate, this.propsToOnDisableClick(this.props))) {
+    if (isQuarterDisabled(labelDate, this.props)) {
       return;
     }
 
@@ -762,7 +673,7 @@ export default class Month extends Component<MonthProps> {
     return {
       isDisabled:
         ((minDate || maxDate || excludeDates || includeDates) &&
-          isMonthDisabled(labelDate, this.propsToOnDisableClick(this.props))) ??
+          isMonthDisabled(labelDate, this.props)) ??
         false,
       labelDate,
     };
@@ -868,10 +779,7 @@ export default class Month extends Component<MonthProps> {
       {
         "react-datepicker__quarter-text--disabled":
           (minDate || maxDate) &&
-          isQuarterDisabled(
-            setQuarter(day, q),
-            this.propsToOnDisableClick(this.props),
-          ),
+          isQuarterDisabled(setQuarter(day, q), this.props),
         "react-datepicker__quarter-text--selected": selected
           ? this.isSelectedQuarter(day, q, selected)
           : undefined,
@@ -930,16 +838,16 @@ export default class Month extends Component<MonthProps> {
           <div
             ref={this.MONTH_REFS[m]}
             key={j}
-            onClick={(ev) => {
-              this.onMonthClick(ev, m);
+            onClick={(event) => {
+              this.onMonthClick(event, m);
             }}
-            onKeyDown={(ev) => {
-              if (isSpaceKeyDown(ev)) {
-                ev.preventDefault();
-                ev.key = "Enter";
+            onKeyDown={(event) => {
+              if (isSpaceKeyDown(event)) {
+                event.preventDefault();
+                event.key = "Enter";
               }
 
-              this.onMonthKeyDown(ev, m);
+              this.onMonthKeyDown(event, m);
             }}
             onMouseEnter={
               !this.props.usePointerEvent
@@ -957,7 +865,9 @@ export default class Month extends Component<MonthProps> {
             role="option"
             aria-label={this.getAriaLabel(m)}
             aria-current={this.isCurrentMonth(day, m) ? "date" : undefined}
-            aria-selected={selected && this.isSelectedMonth(day, m, selected)}
+            aria-selected={
+              selected ? this.isSelectedMonth(day, m, selected) : undefined
+            }
           >
             {this.getMonthContent(m)}
           </div>
@@ -976,11 +886,11 @@ export default class Month extends Component<MonthProps> {
             key={j}
             ref={this.QUARTER_REFS[j]}
             role="option"
-            onClick={(ev) => {
-              this.onQuarterClick(ev, q);
+            onClick={(event) => {
+              this.onQuarterClick(event, q);
             }}
-            onKeyDown={(ev) => {
-              this.onQuarterKeyDown(ev, q);
+            onKeyDown={(event) => {
+              this.onQuarterKeyDown(event, q);
             }}
             onMouseEnter={
               !this.props.usePointerEvent
@@ -993,7 +903,9 @@ export default class Month extends Component<MonthProps> {
                 : undefined
             }
             className={this.getQuarterClassNames(q)}
-            aria-selected={selected && this.isSelectedQuarter(day, q, selected)}
+            aria-selected={
+              selected ? this.isSelectedQuarter(day, q, selected) : undefined
+            }
             tabIndex={Number(this.getQuarterTabIndex(q))}
             aria-current={this.isCurrentQuarter(day, q) ? "date" : undefined}
           >
diff --git a/src/month_dropdown.tsx b/src/month_dropdown.tsx
index d08497cc..902e9640 100644
--- a/src/month_dropdown.tsx
+++ b/src/month_dropdown.tsx
@@ -12,10 +12,14 @@ interface MonthDropdownOptionsProps
 
 const WrappedMonthDropdownOptions = onClickOutside(MonthDropdownOptions);
 
-interface MonthDropdownProps extends Pick<MonthDropdownOptionsProps, "month"> {
+interface MonthDropdownProps
+  extends Omit<
+    MonthDropdownOptionsProps,
+    "monthNames" | "onChange" | "onCancel"
+  > {
   dropdownMode: "scroll" | "select";
   locale?: Locale;
-  onChange: (month: number | string) => void;
+  onChange: (month: number) => void;
   useShortMonthInDropdown?: boolean;
 }
 
@@ -44,7 +48,7 @@ export default class MonthDropdown extends Component<
     <select
       value={this.props.month}
       className="react-datepicker__month-select"
-      onChange={(e) => this.onChange(e.target.value)}
+      onChange={(e) => this.onChange(parseInt(e.target.value))}
     >
       {this.renderSelectOptions(monthNames)}
     </select>
@@ -67,7 +71,7 @@ export default class MonthDropdown extends Component<
   renderDropdown = (monthNames: string[]): JSX.Element => (
     <WrappedMonthDropdownOptions
       key="dropdown"
-      month={this.props.month}
+      {...this.props}
       monthNames={monthNames}
       onChange={this.onChange}
       onCancel={this.toggleDropdown}
@@ -83,7 +87,7 @@ export default class MonthDropdown extends Component<
     return result;
   };
 
-  onChange = (month: number | string): void => {
+  onChange = (month: number): void => {
     this.toggleDropdown();
     if (month !== this.props.month) {
       this.props.onChange(month);
diff --git a/src/month_year_dropdown.tsx b/src/month_year_dropdown.tsx
index 2622eb6c..61e1c871 100644
--- a/src/month_year_dropdown.tsx
+++ b/src/month_year_dropdown.tsx
@@ -10,6 +10,7 @@ import {
   isSameYear,
   newDate,
   getTime,
+  type Locale,
 } from "./date_utils";
 
 interface MonthYearDropdownOptionsProps
@@ -20,17 +21,10 @@ const WrappedMonthYearDropdownOptions = onClickOutside(
 );
 
 interface MonthYearDropdownProps
-  extends Pick<
-    MonthYearDropdownOptionsProps,
-    | "date"
-    | "dateFormat"
-    | "minDate"
-    | "maxDate"
-    | "scrollableMonthYearDropdown"
-    | "locale"
-  > {
+  extends Omit<MonthYearDropdownOptionsProps, "onChange" | "onCancel"> {
   dropdownMode: "scroll" | "select";
   onChange: (monthYear: Date) => void;
+  locale?: Locale;
 }
 
 interface MonthYearDropdownState {
@@ -64,8 +58,8 @@ export default class MonthYearDropdown extends Component<
     return options;
   };
 
-  onSelectChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {
-    this.onChange(e.target.value);
+  onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
+    this.onChange(parseInt(event.target.value));
   };
 
   renderSelectMode = (): JSX.Element => (
@@ -103,14 +97,9 @@ export default class MonthYearDropdown extends Component<
   renderDropdown = (): JSX.Element => (
     <WrappedMonthYearDropdownOptions
       key="dropdown"
-      date={this.props.date}
-      dateFormat={this.props.dateFormat}
+      {...this.props}
       onChange={this.onChange}
       onCancel={this.toggleDropdown}
-      minDate={this.props.minDate}
-      maxDate={this.props.maxDate}
-      scrollableMonthYearDropdown={this.props.scrollableMonthYearDropdown}
-      locale={this.props.locale}
     />
   );
 
@@ -123,14 +112,10 @@ export default class MonthYearDropdown extends Component<
     return result;
   };
 
-  onChange = (monthYearPoint: number | string): void => {
+  onChange = (monthYearPoint: number): void => {
     this.toggleDropdown();
 
-    const changedDate = newDate(
-      typeof monthYearPoint === "string"
-        ? parseInt(monthYearPoint)
-        : monthYearPoint,
-    );
+    const changedDate = newDate(monthYearPoint);
 
     if (
       isSameYear(this.props.date, changedDate) &&
diff --git a/src/portal.tsx b/src/portal.tsx
index d62260a8..6e17a569 100644
--- a/src/portal.tsx
+++ b/src/portal.tsx
@@ -18,9 +18,6 @@ interface PortalProps {
  * @property {ShadowRoot} [props.portalHost] - The DOM node to host the `Portal`.
  */
 class Portal extends Component<PortalProps> {
-  private el: HTMLDivElement;
-  private portalRoot!: HTMLElement | null;
-
   constructor(props: PortalProps) {
     super(props);
     this.el = document.createElement("div");
@@ -44,6 +41,9 @@ class Portal extends Component<PortalProps> {
     }
   }
 
+  private el: HTMLDivElement;
+  private portalRoot: HTMLElement | null = null;
+
   render() {
     return ReactDOM.createPortal(this.props.children, this.el);
   }
diff --git a/src/tab_loop.tsx b/src/tab_loop.tsx
index af0cbf32..78855a94 100644
--- a/src/tab_loop.tsx
+++ b/src/tab_loop.tsx
@@ -45,14 +45,14 @@ export default class TabLoop extends Component<TabLoopProps> {
     enableTabLoop: true,
   };
 
-  private tabLoopRef: React.RefObject<HTMLDivElement>;
-
   constructor(props: TabLoopProps) {
     super(props);
 
     this.tabLoopRef = createRef();
   }
 
+  private tabLoopRef: React.RefObject<HTMLDivElement>;
+
   /**
    * `getTabChildren` is a method of the `TabLoop` class that retrieves all tabbable children of the component.
    *
diff --git a/src/time.tsx b/src/time.tsx
index 29d3e9d0..c2ba6af2 100644
--- a/src/time.tsx
+++ b/src/time.tsx
@@ -13,21 +13,21 @@ import {
   isSameMinute,
   getSeconds,
   type Locale,
+  type TimeFilterOptions,
 } from "./date_utils";
 
-interface TimeProps {
+interface TimeProps
+  extends Pick<
+    TimeFilterOptions,
+    "minTime" | "maxTime" | "excludeTimes" | "includeTimes" | "filterTime"
+  > {
   format?: string;
-  includeTimes?: Date[];
   intervals: number;
-  selected?: Date;
+  selected?: Date | null;
   openToDate?: Date;
   onChange?: (time: Date) => void;
   timeClassName?: (time: Date) => string;
   todayButton?: React.ReactNode;
-  minTime?: Date;
-  maxTime?: Date;
-  excludeTimes?: Date[];
-  filterTime?: (time: Date) => boolean;
   monthRef?: HTMLDivElement;
   timeCaption?: string;
   injectTimes?: Date[];
@@ -62,12 +62,6 @@ export default class Time extends Component<TimeProps, TimeState> {
     height: null,
   };
 
-  private header?: HTMLDivElement;
-
-  private list?: HTMLUListElement;
-
-  private centerLi?: HTMLLIElement;
-
   componentDidMount(): void {
     // code to ensure selected time will always be in focus within time window when it first appears
     this.scrollToTheSelectedTime();
@@ -78,6 +72,12 @@ export default class Time extends Component<TimeProps, TimeState> {
     }
   }
 
+  private header?: HTMLDivElement;
+
+  private list?: HTMLUListElement;
+
+  private centerLi?: HTMLLIElement;
+
   scrollToTheSelectedTime = (): void => {
     requestAnimationFrame((): void => {
       if (!this.list) return;
@@ -234,8 +234,8 @@ export default class Time extends Component<TimeProps, TimeState> {
               this.centerLi = li;
             }
           }}
-          onKeyDown={(ev: React.KeyboardEvent<HTMLLIElement>): void => {
-            this.handleOnKeyDown(ev, time);
+          onKeyDown={(event: React.KeyboardEvent<HTMLLIElement>): void => {
+            this.handleOnKeyDown(event, time);
           }}
           tabIndex={time === timeToFocus ? 0 : -1}
           role="option"
diff --git a/src/week.tsx b/src/week.tsx
index 8ffd3d19..946e01c3 100644
--- a/src/week.tsx
+++ b/src/week.tsx
@@ -17,10 +17,7 @@ interface WeekProps
       | "onClick"
       | "onMouseEnter"
     >,
-    Omit<
-      WeekNumberProps,
-      "weekNumber" | "date" | "onClick" | "handleOnKeyDown"
-    > {
+    Omit<WeekNumberProps, "weekNumber" | "date" | "onClick"> {
   day: Date;
   chooseDayAriaLabelPrefix: DayProps["ariaLabelPrefixWhenEnabled"];
   disabledDayAriaLabelPrefix: DayProps["ariaLabelPrefixWhenDisabled"];
@@ -93,18 +90,10 @@ export default class Week extends Component<WeekProps> {
       days.push(
         <WeekNumber
           key="W"
+          {...this.props}
           weekNumber={weekNumber}
           date={startOfWeek}
           onClick={onClickAction}
-          selected={this.props.selected}
-          preSelection={this.props.preSelection}
-          ariaLabelPrefix={this.props.ariaLabelPrefix}
-          showWeekPicker={this.props.showWeekPicker}
-          showWeekNumber={this.props.showWeekNumber}
-          disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
-          handleOnKeyDown={this.props.handleOnKeyDown}
-          isInputFocused={this.props.isInputFocused}
-          containerRef={this.props.containerRef}
         />,
       );
     }
@@ -113,49 +102,13 @@ export default class Week extends Component<WeekProps> {
         const day = addDays(startOfWeek, offset);
         return (
           <Day
+            {...this.props}
             ariaLabelPrefixWhenEnabled={this.props.chooseDayAriaLabelPrefix}
             ariaLabelPrefixWhenDisabled={this.props.disabledDayAriaLabelPrefix}
             key={day.valueOf()}
             day={day}
-            month={this.props.month}
             onClick={this.handleDayClick.bind(this, day)}
-            usePointerEvent={this.props.usePointerEvent}
             onMouseEnter={this.handleDayMouseEnter.bind(this, day)}
-            minDate={this.props.minDate}
-            maxDate={this.props.maxDate}
-            calendarStartDay={this.props.calendarStartDay}
-            excludeDates={this.props.excludeDates}
-            excludeDateIntervals={this.props.excludeDateIntervals}
-            includeDates={this.props.includeDates}
-            includeDateIntervals={this.props.includeDateIntervals}
-            highlightDates={this.props.highlightDates}
-            holidays={this.props.holidays}
-            selectingDate={this.props.selectingDate}
-            filterDate={this.props.filterDate}
-            preSelection={this.props.preSelection}
-            selected={this.props.selected}
-            selectsStart={this.props.selectsStart}
-            selectsEnd={this.props.selectsEnd}
-            selectsRange={this.props.selectsRange}
-            showWeekPicker={this.props.showWeekPicker}
-            showWeekNumber={this.props.showWeekNumber}
-            selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
-            selectsMultiple={this.props.selectsMultiple}
-            selectedDates={this.props.selectedDates}
-            startDate={this.props.startDate}
-            endDate={this.props.endDate}
-            dayClassName={this.props.dayClassName}
-            renderDayContents={this.props.renderDayContents}
-            disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
-            handleOnKeyDown={this.props.handleOnKeyDown}
-            containerRef={this.props.containerRef}
-            inline={this.props.inline}
-            shouldFocusDayInline={this.props.shouldFocusDayInline}
-            monthShowsDuplicateDaysEnd={this.props.monthShowsDuplicateDaysEnd}
-            monthShowsDuplicateDaysStart={
-              this.props.monthShowsDuplicateDaysStart
-            }
-            locale={this.props.locale}
           />
         );
       }),
diff --git a/src/week_number.tsx b/src/week_number.tsx
index 85c680be..6cf1295e 100644
--- a/src/week_number.tsx
+++ b/src/week_number.tsx
@@ -7,8 +7,8 @@ interface WeekNumberProps {
   date: Date;
   onClick: React.MouseEventHandler<HTMLDivElement>;
   ariaLabelPrefix?: string;
-  selected?: Date;
-  preSelection?: Date;
+  selected?: Date | null;
+  preSelection?: Date | null;
   showWeekPicker?: boolean;
   showWeekNumber?: boolean;
   disabledKeyboardNavigation?: boolean;
@@ -16,6 +16,7 @@ interface WeekNumberProps {
   shouldFocusDayInline?: boolean;
   handleOnKeyDown: React.KeyboardEventHandler<HTMLDivElement>;
   containerRef?: React.RefObject<HTMLDivElement>;
+  // eslint-disable-next-line react/no-unused-prop-types
   isInputFocused?: boolean;
 }
 
diff --git a/src/with_floating.tsx b/src/with_floating.tsx
index 7c9b15ca..cf68cc15 100644
--- a/src/with_floating.tsx
+++ b/src/with_floating.tsx
@@ -8,6 +8,7 @@ import {
   Middleware,
   Placement,
   type UseFloatingOptions,
+  // eslint-disable-next-line unused-imports/no-unused-imports
   type ReferenceType,
 } from "@floating-ui/react";
 
diff --git a/src/year.tsx b/src/year.tsx
index 7277f4cd..14ac97c1 100644
--- a/src/year.tsx
+++ b/src/year.tsx
@@ -1,5 +1,6 @@
 import React, { Component, createRef } from "react";
 import {
+  type DateFilterOptions,
   addYears,
   getStartOfYear,
   getYear,
@@ -19,8 +20,12 @@ import { clsx } from "clsx";
 
 const VERTICAL_NAVIGATION_OFFSET = 3;
 
-interface YearProps {
-  clearSelectingDate?: () => void;
+interface YearProps
+  extends Pick<
+    DateFilterOptions,
+    "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate"
+  > {
+  clearSelectingDate?: VoidFunction;
   date?: Date;
   disabledKeyboardNavigation?: boolean;
   endDate?: Date;
@@ -30,19 +35,17 @@ interface YearProps {
       | React.MouseEvent<HTMLDivElement>
       | React.KeyboardEvent<HTMLDivElement>,
   ) => void;
-  preSelection?: Date;
-  setPreSelection?: (date: Date) => void;
-  selected?: Date;
+  preSelection?: Date | null;
+  setPreSelection?: (date?: Date | null) => void;
+  selected?: Date | null;
   inline?: boolean;
-  maxDate?: Date;
-  minDate?: Date;
   usePointerEvent?: boolean;
   onYearMouseEnter: (
-    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
+    event: React.MouseEvent<HTMLDivElement, MouseEvent>,
     year: number,
   ) => void;
   onYearMouseLeave: (
-    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
+    event: React.MouseEvent<HTMLDivElement, MouseEvent>,
     year: number,
   ) => void;
   selectingDate?: Date;
@@ -51,11 +54,8 @@ interface YearProps {
   selectsStart?: boolean;
   selectsRange?: boolean;
   startDate?: Date;
-  excludeDates?: Date[] | { date: Date; message?: string }[];
-  includeDates?: [];
-  filterDate?: (date: Date) => boolean;
   yearItemNumber?: number;
-  handleOnKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
+  handleOnKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
   yearClassName?: (date: Date) => string;
 }
 
@@ -64,7 +64,7 @@ interface YearProps {
  *
  * @class
  * @param {YearProps} props - The properties that define the `Year` component.
- * @property {() => void} [props.clearSelectingDate] - Function to clear the selected date.
+ * @property {VoidFunction} [props.clearSelectingDate] - Function to clear the selected date.
  * @property {Date} [props.date] - The currently selected date.
  * @property {boolean} [props.disabledKeyboardNavigation] - If true, keyboard navigation is disabled.
  * @property {Date} [props.endDate] - The end date in a range selection.
@@ -215,8 +215,8 @@ export default class Year extends Component<YearProps> {
   isKeyboardSelected = (y: number) => {
     if (
       this.props.date === undefined ||
-      this.props.selected === undefined ||
-      this.props.preSelection === undefined
+      this.props.selected == null ||
+      this.props.preSelection == null
     ) {
       return;
     }
@@ -230,7 +230,7 @@ export default class Year extends Component<YearProps> {
   };
 
   onYearClick = (
-    e:
+    event:
       | React.MouseEvent<HTMLDivElement, MouseEvent>
       | React.KeyboardEvent<HTMLDivElement>,
     y: number,
@@ -239,29 +239,29 @@ export default class Year extends Component<YearProps> {
     if (date === undefined) {
       return;
     }
-    this.handleYearClick(getStartOfYear(setYear(date, y)), e);
+    this.handleYearClick(getStartOfYear(setYear(date, y)), event);
   };
 
-  onYearKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, y: number) => {
-    const { key } = e;
+  onYearKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, y: number) => {
+    const { key } = event;
     const { date, yearItemNumber, handleOnKeyDown } = this.props;
 
     if (key !== "Tab") {
       // preventDefault on tab event blocks focus change
-      e.preventDefault();
+      event.preventDefault();
     }
 
     if (!this.props.disabledKeyboardNavigation) {
       switch (key) {
         case "Enter":
-          if (this.props.selected === undefined) {
+          if (this.props.selected == null) {
             break;
           }
-          this.onYearClick(e, y);
+          this.onYearClick(event, y);
           this.props.setPreSelection?.(this.props.selected);
           break;
         case "ArrowRight":
-          if (this.props.preSelection === undefined) {
+          if (this.props.preSelection == null) {
             break;
           }
           this.handleYearNavigation(
@@ -270,7 +270,7 @@ export default class Year extends Component<YearProps> {
           );
           break;
         case "ArrowLeft":
-          if (this.props.preSelection === undefined) {
+          if (this.props.preSelection == null) {
             break;
           }
           this.handleYearNavigation(
@@ -282,7 +282,7 @@ export default class Year extends Component<YearProps> {
           if (
             date === undefined ||
             yearItemNumber === undefined ||
-            this.props.preSelection === undefined
+            this.props.preSelection == null
           ) {
             break;
           }
@@ -312,7 +312,7 @@ export default class Year extends Component<YearProps> {
           if (
             date === undefined ||
             yearItemNumber === undefined ||
-            this.props.preSelection === undefined
+            this.props.preSelection == null
           ) {
             break;
           }
@@ -341,7 +341,7 @@ export default class Year extends Component<YearProps> {
       }
     }
 
-    handleOnKeyDown && handleOnKeyDown(e);
+    handleOnKeyDown && handleOnKeyDown(event);
   };
 
   getYearClassNames = (y: number) => {
@@ -366,20 +366,7 @@ export default class Year extends Component<YearProps> {
           : undefined,
         "react-datepicker__year-text--disabled":
           (minDate || maxDate || excludeDates || includeDates || filterDate) &&
-          isYearDisabled(y, {
-            minDate: this.props.minDate,
-            maxDate: this.props.maxDate,
-            excludeDates: this.props.excludeDates?.reduce((acc, item) => {
-              if (item instanceof Date) {
-                acc.push(item);
-              } else {
-                acc.push(item.date);
-              }
-              return acc;
-            }, [] as Date[]),
-            includeDates: this.props.includeDates,
-            filterDate: this.props.filterDate,
-          }),
+          isYearDisabled(y, this.props),
         "react-datepicker__year-text--keyboard-selected":
           this.isKeyboardSelected(y),
         "react-datepicker__year-text--range-start": this.isRangeStart(y),
@@ -399,7 +386,7 @@ export default class Year extends Component<YearProps> {
   getYearTabIndex = (y: number) => {
     if (
       this.props.disabledKeyboardNavigation ||
-      this.props.preSelection === undefined
+      this.props.preSelection == null
     ) {
       return "-1";
     }
@@ -434,37 +421,37 @@ export default class Year extends Component<YearProps> {
       yearsList.push(
         <div
           ref={this.YEAR_REFS[y - startPeriod]}
-          onClick={(ev) => {
-            this.onYearClick(ev, y);
+          onClick={(event) => {
+            this.onYearClick(event, y);
           }}
-          onKeyDown={(ev) => {
-            if (isSpaceKeyDown(ev)) {
-              ev.preventDefault();
-              ev.key = "Enter";
+          onKeyDown={(event) => {
+            if (isSpaceKeyDown(event)) {
+              event.preventDefault();
+              event.key = "Enter";
             }
 
-            this.onYearKeyDown(ev, y);
+            this.onYearKeyDown(event, y);
           }}
           tabIndex={Number(this.getYearTabIndex(y))}
           className={this.getYearClassNames(y)}
           onMouseEnter={
             !this.props.usePointerEvent
-              ? (ev) => onYearMouseEnter(ev, y)
+              ? (event) => onYearMouseEnter(event, y)
               : undefined
           }
           onPointerEnter={
             this.props.usePointerEvent
-              ? (ev) => onYearMouseEnter(ev, y)
+              ? (event) => onYearMouseEnter(event, y)
               : undefined
           }
           onMouseLeave={
             !this.props.usePointerEvent
-              ? (ev) => onYearMouseLeave(ev, y)
+              ? (event) => onYearMouseLeave(event, y)
               : undefined
           }
           onPointerLeave={
             this.props.usePointerEvent
-              ? (ev) => onYearMouseLeave(ev, y)
+              ? (event) => onYearMouseLeave(event, y)
               : undefined
           }
           key={y}
diff --git a/src/year_dropdown.tsx b/src/year_dropdown.tsx
index 37c573bf..2beeaf89 100644
--- a/src/year_dropdown.tsx
+++ b/src/year_dropdown.tsx
@@ -9,20 +9,13 @@ interface YearDropdownOptionsProps
 const WrappedYearDropdownOptions = onClickOutside(YearDropdownOptions);
 
 interface YearDropdownProps
-  extends Pick<
-    YearDropdownOptionsProps,
-    | "minDate"
-    | "maxDate"
-    | "scrollableYearDropdown"
-    | "year"
-    | "yearDropdownItemNumber"
-  > {
+  extends Omit<YearDropdownOptionsProps, "onChange" | "onCancel"> {
   adjustDateOnChange?: boolean;
   dropdownMode: "scroll" | "select";
-  onChange: (year: number | string) => void;
-  date?: Date;
-  onSelect?: (date?: Date, event?: React.MouseEvent<HTMLDivElement>) => void;
-  setOpen?: (open?: boolean) => void;
+  onChange: (year: number) => void;
+  date: Date;
+  onSelect?: (date: Date, event?: React.MouseEvent<HTMLDivElement>) => void;
+  setOpen?: (open: boolean) => void;
 }
 
 interface YearDropdownState {
@@ -56,8 +49,8 @@ export default class YearDropdown extends Component<
     return options;
   };
 
-  onSelectChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {
-    this.onChange(e.target.value);
+  onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
+    this.onChange(parseInt(event.target.value));
   };
 
   renderSelectMode = (): JSX.Element => (
@@ -89,13 +82,9 @@ export default class YearDropdown extends Component<
   renderDropdown = (): JSX.Element => (
     <WrappedYearDropdownOptions
       key="dropdown"
-      year={this.props.year}
+      {...this.props}
       onChange={this.onChange}
       onCancel={this.toggleDropdown}
-      minDate={this.props.minDate}
-      maxDate={this.props.maxDate}
-      scrollableYearDropdown={this.props.scrollableYearDropdown}
-      yearDropdownItemNumber={this.props.yearDropdownItemNumber}
     />
   );
 
@@ -108,7 +97,7 @@ export default class YearDropdown extends Component<
     return result;
   };
 
-  onChange = (year: number | string): void => {
+  onChange = (year: number): void => {
     this.toggleDropdown();
     if (year === this.props.year) return;
     this.props.onChange(year);
@@ -128,14 +117,14 @@ export default class YearDropdown extends Component<
   };
 
   handleYearChange = (
-    date?: Date,
+    date: Date,
     event?: React.MouseEvent<HTMLDivElement>,
   ): void => {
     this.onSelect(date, event);
     this.setOpen();
   };
 
-  onSelect = (date?: Date, event?: React.MouseEvent<HTMLDivElement>): void => {
+  onSelect = (date: Date, event?: React.MouseEvent<HTMLDivElement>): void => {
     if (this.props.onSelect) {
       this.props.onSelect(date, event);
     }
diff --git a/src/year_dropdown_options.tsx b/src/year_dropdown_options.tsx
index 0aa875df..42434925 100644
--- a/src/year_dropdown_options.tsx
+++ b/src/year_dropdown_options.tsx
@@ -47,8 +47,6 @@ export default class YearDropdownOptions extends Component<
   YearDropdownOptionsProps,
   YearDropdownOptionsState
 > {
-  dropdownRef: React.RefObject<HTMLDivElement>;
-
   constructor(props: YearDropdownOptionsProps) {
     super(props);
     const { yearDropdownItemNumber, scrollableYearDropdown } = props;
@@ -87,6 +85,8 @@ export default class YearDropdownOptions extends Component<
     }
   }
 
+  dropdownRef: React.RefObject<HTMLDivElement>;
+
   renderOptions = (): JSX.Element[] => {
     const selectedYear = this.props.year;
     const options = this.state.yearsList.map((year) => (
diff --git a/tsconfig.json b/tsconfig.json
index 1a69dd83..2eb61d0d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,7 +10,7 @@
     "noUncheckedIndexedAccess": true,
     "module": "esnext",
     "target": "es5",
-    "lib": ["es6", "dom"],
+    "lib": ["ES2021", "dom"],
     "sourceMap": true,
     "jsx": "react",
     "moduleResolution": "node",
@@ -20,7 +20,8 @@
     "noImplicitAny": true,
     "strictNullChecks": true,
     "esModuleInterop": true,
-    "baseUrl": "src/"
+    "baseUrl": "src/",
+    "typeRoots": ["./src/@types"]
   },
   "include": ["src/**/*"],
   "exclude": ["node_modules", "dist"]
diff --git a/yarn.lock b/yarn.lock
index 1601141d..8552163f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2029,9 +2029,9 @@
     parse5 "^7.0.0"
 
 "@types/node@*", "@types/node@20":
-  version "20.12.12"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050"
-  integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==
+  version "20.12.11"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.11.tgz#c4ef00d3507000d17690643278a60dc55a9dc9be"
+  integrity sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==
   dependencies:
     undici-types "~5.26.4"
 
@@ -2891,9 +2891,9 @@ core-js-compat@^3.31.0, core-js-compat@^3.36.1:
     browserslist "^4.23.0"
 
 core-js@^3.29.1:
-  version "3.37.1"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.1.tgz#d21751ddb756518ac5a00e4d66499df981a62db9"
-  integrity sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==
+  version "3.37.0"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.0.tgz#d8dde58e91d156b2547c19d8a4efd5c7f6c426bb"
+  integrity sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==
 
 cosmiconfig@^9.0.0:
   version "9.0.0"


Test patch:
diff --git a/test/calendar_test.test.js b/test/calendar_test.test.js
index 3def75f2..c5c86a7b 100644
--- a/test/calendar_test.test.js
+++ b/test/calendar_test.test.js
@@ -5,7 +5,7 @@
 import React from "react";
 import Calendar from "../src/calendar";
 import { render, fireEvent, act, waitFor } from "@testing-library/react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import * as utils from "../src/date_utils";
 import { eo } from "date-fns/locale/eo";
 import { fi } from "date-fns/locale/fi";
@@ -463,9 +463,9 @@ describe("Calendar", () => {
         nextYearButtonDisabled: expect.any(Boolean),
         prevYearButtonDisabled: expect.any(Boolean),
         isRenderAriaLiveMessage: expect.any(Boolean),
-        monthContainer: null,
+        monthContainer: undefined,
         monthDate: expect.any(Date),
-        selectingDate: null,
+        selectingDate: undefined,
       };
 
       expect(renderCustomHeader).toHaveBeenCalledWith(match);
@@ -1185,7 +1185,11 @@ describe("Calendar", () => {
       const select = calendar
         .querySelector(".react-datepicker__month-dropdown-container")
         .querySelector("select");
-      fireEvent.change(select);
+      fireEvent.change(select, {
+        target: {
+          value: Array.from(select.querySelectorAll("option")).at(-2).value,
+        },
+      });
 
       expect(onMonthChangeSpy).toHaveBeenCalled();
     });
@@ -1214,7 +1218,11 @@ describe("Calendar", () => {
       const select = calendar
         .querySelector(".react-datepicker__year-dropdown-container")
         .querySelector("select");
-      fireEvent.change(select);
+      fireEvent.change(select, {
+        target: {
+          value: Array.from(select.querySelectorAll("option")).at(-2).value,
+        },
+      });
 
       expect(onYearChangeSpy).toHaveBeenCalled();
     });
diff --git a/test/datepicker_test.test.js b/test/datepicker_test.test.js
index b736673c..b9608812 100644
--- a/test/datepicker_test.test.js
+++ b/test/datepicker_test.test.js
@@ -1,7 +1,7 @@
 import React from "react";
 import { enUS, enGB } from "date-fns/locale";
 import { render, act, waitFor, fireEvent } from "@testing-library/react";
-import DatePicker, { registerLocale } from "../src/index.jsx";
+import DatePicker, { registerLocale } from "../src/index";
 import TestWrapper from "./test_wrapper.jsx";
 import CustomInput from "./helper_components/custom_input.jsx";
 import * as utils from "../src/date_utils.ts";
@@ -279,7 +279,11 @@ describe("DatePicker", () => {
     const yearSelect = container.querySelector(
       ".react-datepicker__year-select",
     );
-    fireEvent.change(yearSelect);
+    fireEvent.change(yearSelect, {
+      target: {
+        value: Array.from(yearSelect.querySelectorAll("option")).at(-2).value,
+      },
+    });
     expect(onYearChangeSpy).toHaveBeenCalled();
   });
 
diff --git a/test/exclude_dates.test.js b/test/exclude_dates.test.js
index d59e187d..82e81aed 100644
--- a/test/exclude_dates.test.js
+++ b/test/exclude_dates.test.js
@@ -1,6 +1,6 @@
 import React from "react";
 import { render } from "@testing-library/react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import { subDays } from "date-fns";
 
 describe("DatePicker", () => {
diff --git a/test/exclude_time_period_test.test.js b/test/exclude_time_period_test.test.js
index d2ae3375..0bd568c4 100644
--- a/test/exclude_time_period_test.test.js
+++ b/test/exclude_time_period_test.test.js
@@ -1,7 +1,7 @@
 import React from "react";
 import { render } from "@testing-library/react";
 import * as utils from "../src/date_utils";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 
 describe("DatePicker", () => {
   it("should only display times between minTime and maxTime", () => {
diff --git a/test/exclude_times_test.test.js b/test/exclude_times_test.test.js
index e07cbc22..46d88a67 100644
--- a/test/exclude_times_test.test.js
+++ b/test/exclude_times_test.test.js
@@ -1,7 +1,7 @@
 import React from "react";
 import { render } from "@testing-library/react";
 import { setTime, newDate } from "../src/date_utils";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 
 describe("DatePicker", () => {
   let now, excludeTimes;
diff --git a/test/min_time_test.test.js b/test/min_time_test.test.js
index d8bcbc20..7d19b27f 100644
--- a/test/min_time_test.test.js
+++ b/test/min_time_test.test.js
@@ -1,5 +1,5 @@
 import React, { useState } from "react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import { fireEvent, render } from "@testing-library/react";
 
 const DatePickerWithState = (props) => {
diff --git a/test/month_dropdown_test.test.js b/test/month_dropdown_test.test.js
index c7868652..8a5f5487 100644
--- a/test/month_dropdown_test.test.js
+++ b/test/month_dropdown_test.test.js
@@ -243,7 +243,7 @@ describe("MonthDropdown", () => {
         ".react-datepicker__month-select",
       );
       fireEvent.change(select, { target: { value: 9 } });
-      expect(handleChangeResult).toEqual("9");
+      expect(handleChangeResult).toEqual(9);
     });
   });
 });
diff --git a/test/show_time_test.test.js b/test/show_time_test.test.js
index a848cc75..d6a96fe8 100644
--- a/test/show_time_test.test.js
+++ b/test/show_time_test.test.js
@@ -1,6 +1,6 @@
 import React from "react";
 import { render, fireEvent } from "@testing-library/react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import TimeComponent from "../src/time";
 
 describe("DatePicker", () => {
diff --git a/test/time_input_test.test.js b/test/time_input_test.test.js
index 0703d939..6febd90b 100644
--- a/test/time_input_test.test.js
+++ b/test/time_input_test.test.js
@@ -1,6 +1,6 @@
 import React from "react";
 import { render, fireEvent } from "@testing-library/react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import InputTimeComponent from "../src/input_time.tsx";
 import CustomTimeInput from "./helper_components/custom_time_input.jsx";
 
diff --git a/test/timepicker_test.test.js b/test/timepicker_test.test.js
index 3b086ad3..b37b435e 100644
--- a/test/timepicker_test.test.js
+++ b/test/timepicker_test.test.js
@@ -1,5 +1,5 @@
 import React from "react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 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/week_picker_test.test.js b/test/week_picker_test.test.js
index 86aaee4a..a4aed861 100644
--- a/test/week_picker_test.test.js
+++ b/test/week_picker_test.test.js
@@ -1,5 +1,5 @@
 import React from "react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import { render, fireEvent } from "@testing-library/react";
 
 describe("WeekPicker", () => {
diff --git a/test/year_dropdown_test.test.js b/test/year_dropdown_test.test.js
index f52c9b5d..f442a4e2 100644
--- a/test/year_dropdown_test.test.js
+++ b/test/year_dropdown_test.test.js
@@ -134,7 +134,7 @@ describe("YearDropdown", () => {
         ".react-datepicker__year-select",
       );
       fireEvent.change(select, { target: { value: 2014 } });
-      expect(lastOnChangeValue).toEqual("2014");
+      expect(lastOnChangeValue).toEqual(2014);
     });
 
     it("calls the supplied onChange function when a different year is clicked", () => {
diff --git a/test/year_picker_test.test.js b/test/year_picker_test.test.js
index 8d2bb6c3..5c1a6d1c 100644
--- a/test/year_picker_test.test.js
+++ b/test/year_picker_test.test.js
@@ -1,5 +1,5 @@
 import React from "react";
-import DatePicker from "../src/index.jsx";
+import DatePicker from "../src/index";
 import Year from "../src/year.tsx";
 import { render, fireEvent } from "@testing-library/react";
 import * as utils from "../src/date_utils.ts";