From ef6e8878f7ab298ccb52b1fc30a28883cfe91c7c Mon Sep 17 00:00:00 2001 From: ssdwgg <1982549567@qq.com> Date: Sat, 30 May 2026 11:12:58 +0800 Subject: [PATCH] fix(react): type overlay hook component props --- .../__tests__/overlay-hook-types.spec.tsx | 42 +++++++++++++++++++ packages/react/src/hooks/useIonModal.ts | 4 +- packages/react/src/hooks/useIonPopover.ts | 7 +++- .../src/models/ReactComponentOrElement.ts | 3 +- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 packages/react/src/hooks/__tests__/overlay-hook-types.spec.tsx diff --git a/packages/react/src/hooks/__tests__/overlay-hook-types.spec.tsx b/packages/react/src/hooks/__tests__/overlay-hook-types.spec.tsx new file mode 100644 index 00000000000..a915f0abe64 --- /dev/null +++ b/packages/react/src/hooks/__tests__/overlay-hook-types.spec.tsx @@ -0,0 +1,42 @@ +import type React from 'react'; + +import type { useIonModal } from '../useIonModal'; +import type { useIonPopover } from '../useIonPopover'; + +type OverlayComponentProps = { + title: string; + count?: number; +}; + +declare const ModalComponent: React.FC; +declare const PopoverComponent: React.ComponentClass; +declare const modalElement: JSX.Element; +declare const useIonModalForTypeTest: typeof useIonModal; +declare const useIonPopoverForTypeTest: typeof useIonPopover; + +function expectOverlayHookTypes() { + useIonModalForTypeTest(ModalComponent, { title: 'Modal', count: 1 }); + useIonPopoverForTypeTest(PopoverComponent, { title: 'Popover', count: 1 }); + + useIonModalForTypeTest(ModalComponent); + useIonPopoverForTypeTest(PopoverComponent); + + useIonModalForTypeTest(modalElement, { anything: true }); + + // @ts-expect-error component props should match the modal component + useIonModalForTypeTest(ModalComponent, { count: 1 }); + + // @ts-expect-error component props should match the popover component + useIonPopoverForTypeTest(PopoverComponent, { count: 1 }); + + // @ts-expect-error unknown props should not be accepted for typed components + useIonModalForTypeTest(ModalComponent, { title: 'Modal', unknown: true }); +} + +void expectOverlayHookTypes; + +describe('overlay hook types', () => { + it('type checks component props', () => { + expect(true).toBe(true); + }); +}); diff --git a/packages/react/src/hooks/useIonModal.ts b/packages/react/src/hooks/useIonModal.ts index aee3ea40e40..de13f55e00f 100644 --- a/packages/react/src/hooks/useIonModal.ts +++ b/packages/react/src/hooks/useIonModal.ts @@ -3,7 +3,7 @@ import { modalController } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-modal.js'; import { useCallback } from 'react'; -import type { ReactComponentOrElement } from '../models/ReactComponentOrElement'; +import type { ReactComponent, ReactComponentOrElement } from '../models/ReactComponentOrElement'; import type { HookOverlayOptions } from './HookOverlayOptions'; import { useOverlay } from './useOverlay'; @@ -16,6 +16,8 @@ import { useOverlay } from './useOverlay'; * @param componentProps The props that will be passed to the component, if required * @returns Returns the present and dismiss methods in an array */ +export function useIonModal(component: ReactComponent, componentProps?: Props): UseIonModalResult; +export function useIonModal(component: JSX.Element, componentProps?: any): UseIonModalResult; export function useIonModal(component: ReactComponentOrElement, componentProps?: any): UseIonModalResult { const controller = useOverlay( 'IonModal', diff --git a/packages/react/src/hooks/useIonPopover.ts b/packages/react/src/hooks/useIonPopover.ts index c63df7b26e0..7dca712e688 100644 --- a/packages/react/src/hooks/useIonPopover.ts +++ b/packages/react/src/hooks/useIonPopover.ts @@ -3,7 +3,7 @@ import { popoverController } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-popover.js'; import { useCallback } from 'react'; -import type { ReactComponentOrElement } from '../models/ReactComponentOrElement'; +import type { ReactComponent, ReactComponentOrElement } from '../models/ReactComponentOrElement'; import type { HookOverlayOptions } from './HookOverlayOptions'; import { useOverlay } from './useOverlay'; @@ -16,6 +16,11 @@ import { useOverlay } from './useOverlay'; * @param componentProps The props that will be passed to the component, if required * @returns Returns the present and dismiss methods in an array */ +export function useIonPopover( + component: ReactComponent, + componentProps?: Props +): UseIonPopoverResult; +export function useIonPopover(component: JSX.Element, componentProps?: any): UseIonPopoverResult; export function useIonPopover(component: ReactComponentOrElement, componentProps?: any): UseIonPopoverResult { const controller = useOverlay( 'IonPopover', diff --git a/packages/react/src/models/ReactComponentOrElement.ts b/packages/react/src/models/ReactComponentOrElement.ts index 2cb796ed837..3e4b12a1ba5 100644 --- a/packages/react/src/models/ReactComponentOrElement.ts +++ b/packages/react/src/models/ReactComponentOrElement.ts @@ -1,3 +1,4 @@ import type React from 'react'; -export type ReactComponentOrElement = React.ComponentClass | React.FC | JSX.Element; +export type ReactComponent = React.ComponentClass | React.FC; +export type ReactComponentOrElement = ReactComponent | JSX.Element;