From f1d5a925e23185f90d8eede9af233faf7d5223b3 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 14 Oct 2026 22:44:19 +0530 Subject: [PATCH 1/5] Block Editor: Convert InspectorControls to TypeScript --- ...r.jsx => block-support-slot-container.tsx} | 3 +- ...anel.jsx => block-support-tools-panel.tsx} | 22 ++++- .../inspector-controls/{fill.jsx => fill.tsx} | 20 +++- .../{groups.js => groups.ts} | 3 +- .../{index.jsx => index.tsx} | 16 +++- .../{last-item.jsx => last-item.tsx} | 5 +- ...over.jsx => list-view-content-popover.tsx} | 17 ++-- .../inspector-controls/{slot.jsx => slot.tsx} | 11 ++- .../components/inspector-controls/types.ts | 96 +++++++++++++++++++ 9 files changed, 168 insertions(+), 25 deletions(-) rename packages/block-editor/src/components/inspector-controls/{block-support-slot-container.jsx => block-support-slot-container.tsx} (87%) rename packages/block-editor/src/components/inspector-controls/{block-support-tools-panel.jsx => block-support-tools-panel.tsx} (81%) rename packages/block-editor/src/components/inspector-controls/{fill.jsx => fill.tsx} (91%) rename packages/block-editor/src/components/inspector-controls/{groups.js => groups.ts} (96%) rename packages/block-editor/src/components/inspector-controls/{index.jsx => index.tsx} (52%) rename packages/block-editor/src/components/inspector-controls/{last-item.jsx => last-item.tsx} (75%) rename packages/block-editor/src/components/inspector-controls/{list-view-content-popover.jsx => list-view-content-popover.tsx} (89%) rename packages/block-editor/src/components/inspector-controls/{slot.jsx => slot.tsx} (82%) create mode 100644 packages/block-editor/src/components/inspector-controls/types.ts diff --git a/packages/block-editor/src/components/inspector-controls/block-support-slot-container.jsx b/packages/block-editor/src/components/inspector-controls/block-support-slot-container.tsx similarity index 87% rename from packages/block-editor/src/components/inspector-controls/block-support-slot-container.jsx rename to packages/block-editor/src/components/inspector-controls/block-support-slot-container.tsx index a12b4c3b223d68..24bf5cfc2bbb8e 100644 --- a/packages/block-editor/src/components/inspector-controls/block-support-slot-container.jsx +++ b/packages/block-editor/src/components/inspector-controls/block-support-slot-container.tsx @@ -1,11 +1,12 @@ import { __experimentalToolsPanelContext as ToolsPanelContext } from '@wordpress/components'; import { useContext, useMemo } from '@wordpress/element'; +import type { BlockSupportSlotContainerProps } from './types'; export default function BlockSupportSlotContainer( { Slot, fillProps, ...props -} ) { +}: BlockSupportSlotContainerProps ) { // Add the toolspanel context provider and value to existing fill props const toolsPanelContext = useContext( ToolsPanelContext ); const computedFillProps = useMemo( diff --git a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.jsx b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.tsx similarity index 81% rename from packages/block-editor/src/components/inspector-controls/block-support-tools-panel.jsx rename to packages/block-editor/src/components/inspector-controls/block-support-tools-panel.tsx index 0209dbeb4f20de..d3b68a3c467712 100644 --- a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.jsx +++ b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.tsx @@ -4,8 +4,16 @@ import { useCallback } from '@wordpress/element'; import { store as blockEditorStore } from '../../store'; import { cleanEmptyObject } from '../../hooks/utils'; import { useToolsPanelDropdownMenuProps } from '../global-styles/utils'; +import type { + BlockSupportResetAllFilter, + BlockSupportToolsPanelProps, +} from './types'; -export default function BlockSupportToolsPanel( { children, group, label } ) { +export default function BlockSupportToolsPanel( { + children, + group, + label, +}: BlockSupportToolsPanelProps ) { const { updateBlockAttributes } = useDispatch( blockEditorStore ); const { getBlockAttributes, @@ -17,17 +25,21 @@ export default function BlockSupportToolsPanel( { children, group, label } ) { const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const panelId = getSelectedBlockClientId(); const resetAll = useCallback( - ( resetFilters = [] ) => { - const newAttributes = {}; + ( resetFilters: BlockSupportResetAllFilter[] = [] ) => { + const newAttributes: Record< + string, + Record< string, unknown > + > = {}; + // The panel only renders while a block is selected. const clientIds = hasMultiSelection() ? getMultiSelectedBlockClientIds() - : [ panelId ]; + : [ panelId! ]; clientIds.forEach( ( clientId ) => { const blockAttributes = getBlockAttributes( clientId ) || {}; const { style } = blockAttributes; - let newBlockAttributes = { style }; + let newBlockAttributes: Record< string, unknown > = { style }; const resetContext = { attributes: blockAttributes, clientId, diff --git a/packages/block-editor/src/components/inspector-controls/fill.jsx b/packages/block-editor/src/components/inspector-controls/fill.tsx similarity index 91% rename from packages/block-editor/src/components/inspector-controls/fill.jsx rename to packages/block-editor/src/components/inspector-controls/fill.tsx index 4657521f90bcf5..0b7465fd79caa7 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.jsx +++ b/packages/block-editor/src/components/inspector-controls/fill.tsx @@ -16,6 +16,11 @@ import { useBlockStyleState, } from '../../hooks/block-style-state'; import { ListViewContentFill } from './list-view-content-popover'; +import type { + InspectorControlsFillProps, + RegisterResetAllProps, + ToolsPanelInspectorControlProps, +} from './types'; const PATTERN_EDITING_GROUPS = [ 'content', 'list' ]; @@ -23,7 +28,7 @@ export default function InspectorControlsFill( { children, group = 'default', resetAllFilter, -} ) { +}: InspectorControlsFillProps ) { const context = useBlockEditContext(); const isSelectedBlock = context[ mayDisplayControlsKey ]; const isPatternEditing = context[ mayDisplayPatternEditingControlsKey ]; @@ -87,7 +92,10 @@ export default function InspectorControlsFill( { ); } -function RegisterResetAll( { resetAllFilter, children } ) { +function RegisterResetAll( { + resetAllFilter, + children, +}: RegisterResetAllProps ) { const { registerResetAllFilter, deregisterResetAllFilter } = useContext( ToolsPanelContext ); const selectedState = useBlockStyleState(); @@ -95,7 +103,7 @@ function RegisterResetAll( { resetAllFilter, children } ) { () => scopeResetAllFilterToState( selectedState, resetAllFilter ), [ resetAllFilter, selectedState ] ); - useEffect( () => { + useEffect( (): void | ( () => void ) => { if ( scopedResetAllFilter && registerResetAllFilter && @@ -114,7 +122,11 @@ function RegisterResetAll( { resetAllFilter, children } ) { return children; } -function ToolsPanelInspectorControl( { children, resetAllFilter, fillProps } ) { +function ToolsPanelInspectorControl( { + children, + resetAllFilter, + fillProps, +}: ToolsPanelInspectorControlProps ) { // `fillProps.forwardedContext` is an array of context provider entries, provided by slot, // that should wrap the fill markup. const { forwardedContext = [] } = fillProps; diff --git a/packages/block-editor/src/components/inspector-controls/groups.js b/packages/block-editor/src/components/inspector-controls/groups.ts similarity index 96% rename from packages/block-editor/src/components/inspector-controls/groups.js rename to packages/block-editor/src/components/inspector-controls/groups.ts index f85b52378b917b..7236cf5776439a 100644 --- a/packages/block-editor/src/components/inspector-controls/groups.js +++ b/packages/block-editor/src/components/inspector-controls/groups.ts @@ -1,4 +1,5 @@ import { createSlotFill } from '@wordpress/components'; +import type { SlotFill } from './types'; const InspectorControlsDefault = createSlotFill( 'InspectorControls' ); const InspectorControlsAdvanced = createSlotFill( 'InspectorAdvancedControls' ); @@ -26,7 +27,7 @@ const InspectorControlsStyles = createSlotFill( 'InspectorControlsStyles' ); const InspectorControlsEffects = createSlotFill( 'InspectorControlsEffects' ); const InspectorControlsContent = createSlotFill( 'InspectorControlsContent' ); -const groups = { +const groups: Record< string, SlotFill > = { default: InspectorControlsDefault, advanced: InspectorControlsAdvanced, background: InspectorControlsBackground, diff --git a/packages/block-editor/src/components/inspector-controls/index.jsx b/packages/block-editor/src/components/inspector-controls/index.tsx similarity index 52% rename from packages/block-editor/src/components/inspector-controls/index.jsx rename to packages/block-editor/src/components/inspector-controls/index.tsx index ef9e839084fe9e..487114033f8e0e 100644 --- a/packages/block-editor/src/components/inspector-controls/index.jsx +++ b/packages/block-editor/src/components/inspector-controls/index.tsx @@ -1,15 +1,25 @@ +import type { ComponentProps } from 'react'; import InspectorControlsFill from './fill'; import InspectorControlsSlot from './slot'; +import type { InspectorControlsFillProps } from './types'; -const InspectorControls = InspectorControlsFill; +// TypeScript only types a property set on a function declared in this file. +const InspectorControls = + InspectorControlsFill as typeof InspectorControlsFill & { + Slot: typeof InspectorControlsSlot; + }; InspectorControls.Slot = InspectorControlsSlot; // This is just here for backward compatibility. -export const InspectorAdvancedControls = ( props ) => { +export const InspectorAdvancedControls = ( + props: InspectorControlsFillProps +) => { return ; }; -InspectorAdvancedControls.Slot = function Slot( props ) { +InspectorAdvancedControls.Slot = function Slot( + props: ComponentProps< typeof InspectorControlsSlot > +) { return ; }; InspectorAdvancedControls.slotName = 'InspectorAdvancedControls'; diff --git a/packages/block-editor/src/components/inspector-controls/last-item.jsx b/packages/block-editor/src/components/inspector-controls/last-item.tsx similarity index 75% rename from packages/block-editor/src/components/inspector-controls/last-item.jsx rename to packages/block-editor/src/components/inspector-controls/last-item.tsx index 6375afbb33996f..379b8cacfc184a 100644 --- a/packages/block-editor/src/components/inspector-controls/last-item.jsx +++ b/packages/block-editor/src/components/inspector-controls/last-item.tsx @@ -1,4 +1,5 @@ import { createSlotFill } from '@wordpress/components'; +import type { ComponentProps } from 'react'; import { useBlockEditContext, mayDisplayControlsKey, @@ -6,7 +7,7 @@ import { const { Fill, Slot } = createSlotFill( Symbol( 'InspectorControlsLastItem' ) ); -const InspectorControlsLastItem = ( props ) => { +const InspectorControlsLastItem = ( props: ComponentProps< typeof Fill > ) => { const context = useBlockEditContext(); if ( ! context[ mayDisplayControlsKey ] ) { return null; @@ -14,7 +15,7 @@ const InspectorControlsLastItem = ( props ) => { return ; }; InspectorControlsLastItem.Slot = function InspectorControlsLastItemSlot( - props + props: ComponentProps< typeof Slot > ) { return ; }; diff --git a/packages/block-editor/src/components/inspector-controls/list-view-content-popover.jsx b/packages/block-editor/src/components/inspector-controls/list-view-content-popover.tsx similarity index 89% rename from packages/block-editor/src/components/inspector-controls/list-view-content-popover.jsx rename to packages/block-editor/src/components/inspector-controls/list-view-content-popover.tsx index 32037da2cf7e3d..c350863ccbfdb1 100644 --- a/packages/block-editor/src/components/inspector-controls/list-view-content-popover.jsx +++ b/packages/block-editor/src/components/inspector-controls/list-view-content-popover.tsx @@ -8,8 +8,7 @@ import { useViewportMatch } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; - -/** @typedef {import('@wordpress/element').RefObject} RefObject */ +import type { ListViewContentPopoverProps } from './types'; // Create private slot-fill for ListViewContentPanel const LIST_VIEW_CONTENT_PANEL_SLOT = Symbol( 'ListViewContentPopover' ); @@ -23,7 +22,7 @@ function useInspectorPopoverPlacement() { return ! isMobile ? { popoverProps: { - placement: 'left-start', + placement: 'left-start' as const, offset: 35, resize: false, }, @@ -38,10 +37,12 @@ function useInspectorPopoverPlacement() { * List View block support. The routing is handled automatically by * `InspectorControlsFill`. * - * @param {Object} props - * @param {RefObject} props.listViewRef Ref to the List View slot for the block inspector. + * @param props + * @param props.listViewRef Ref to the List View slot for the block inspector. */ -export function ListViewContentPopover( { listViewRef } ) { +export function ListViewContentPopover( { + listViewRef, +}: ListViewContentPopoverProps ) { const { popoverProps } = useInspectorPopoverPlacement(); const fills = useSlotFills( LIST_VIEW_CONTENT_PANEL_SLOT ); const hasFills = Boolean( fills && fills.length ); @@ -58,7 +59,9 @@ export function ListViewContentPopover( { listViewRef } ) { }, [] ); // Query DOM for the selected block row element in List View. - const [ anchorElement, setAnchorElement ] = useState( null ); + const [ anchorElement, setAnchorElement ] = useState< Element | null >( + null + ); useLayoutEffect( () => { if ( ! selectedClientId || ! listViewRef?.current ) { diff --git a/packages/block-editor/src/components/inspector-controls/slot.jsx b/packages/block-editor/src/components/inspector-controls/slot.tsx similarity index 82% rename from packages/block-editor/src/components/inspector-controls/slot.jsx rename to packages/block-editor/src/components/inspector-controls/slot.tsx index 922b8cbd367e69..e5efd80707b0c3 100644 --- a/packages/block-editor/src/components/inspector-controls/slot.jsx +++ b/packages/block-editor/src/components/inspector-controls/slot.tsx @@ -1,13 +1,20 @@ import { __experimentalUseSlotFills as useSlotFills } from '@wordpress/components'; import { forwardRef } from '@wordpress/element'; import warning from '@wordpress/warning'; +import type { ForwardedRef } from 'react'; import BlockSupportToolsPanel from './block-support-tools-panel'; import BlockSupportSlotContainer from './block-support-slot-container'; import groups from './groups'; +import type { InspectorControlsSlotProps } from './types'; function InspectorControlsSlot( - { group = 'default', label, fillProps, ...props }, - ref + { + group = 'default', + label, + fillProps, + ...props + }: InspectorControlsSlotProps, + ref: ForwardedRef< unknown > ) { const slotFill = groups[ group ]; const fills = useSlotFills( slotFill?.name ); diff --git a/packages/block-editor/src/components/inspector-controls/types.ts b/packages/block-editor/src/components/inspector-controls/types.ts new file mode 100644 index 00000000000000..58366a57746014 --- /dev/null +++ b/packages/block-editor/src/components/inspector-controls/types.ts @@ -0,0 +1,96 @@ +import type { ElementType, ReactNode, RefObject } from 'react'; +import type { createSlotFill } from '@wordpress/components'; +import type { ResetAllFilter } from '../../hooks/types'; + +/** + * A slot and fill pair. + */ +export type SlotFill = ReturnType< typeof createSlotFill >; + +/** + * Props of `InspectorControls`. + */ +export interface InspectorControlsFillProps { + children?: ReactNode; + group?: string; + resetAllFilter?: ResetAllFilter; +} + +/** + * Props of `InspectorControls.Slot`. + */ +export interface InspectorControlsSlotProps { + group?: string; + label?: string; + fillProps?: Record< string, unknown >; + [ prop: string ]: unknown; +} + +/** + * Context providers, with their props, that a slot forwards to its fills. + */ +export type ForwardedContext = Array< + [ ElementType, Record< string, unknown > ] +>; + +/** + * Props of the slot that forwards the tools panel context to its fills. + */ +export interface BlockSupportSlotContainerProps { + Slot: SlotFill[ 'Slot' ]; + fillProps?: { + forwardedContext?: ForwardedContext; + [ prop: string ]: unknown; + }; + [ prop: string ]: unknown; +} + +/** + * The block a reset-all filter resets. + */ +export interface ResetAllContext { + attributes: Record< string, unknown >; + clientId: string; + name: string | null; +} + +/** + * A reset-all filter the block support tools panel runs for each block. + */ +export type BlockSupportResetAllFilter = ( + attributes: Record< string, unknown >, + context: ResetAllContext +) => Record< string, unknown > | undefined; + +/** + * Props of the component that registers a fill's reset-all filter. + */ +export interface RegisterResetAllProps { + resetAllFilter?: ResetAllFilter; + children?: ReactNode; +} + +/** + * Props of the fill content rendered inside a block support tools panel. + */ +export interface ToolsPanelInspectorControlProps { + children?: ReactNode; + resetAllFilter?: ResetAllFilter; + fillProps: { forwardedContext?: ForwardedContext }; +} + +/** + * Props of the tools panel that wraps a labelled InspectorControls slot. + */ +export interface BlockSupportToolsPanelProps { + children?: ReactNode; + group: string; + label: string; +} + +/** + * Props of the List View content popover. + */ +export interface ListViewContentPopoverProps { + listViewRef: RefObject< HTMLElement >; +} From 5de39ee3d4ad403d13d8e3926ec30fff88fa7f91 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 14 Oct 2026 22:44:29 +0530 Subject: [PATCH 2/5] Block Editor: Prune the ESLint suppressions of the converted InspectorControls --- tools/eslint/suppressions.json | 35 ---------------------------------- 1 file changed, 35 deletions(-) diff --git a/tools/eslint/suppressions.json b/tools/eslint/suppressions.json index 0ca5c2c8ff928c..e8dcd3f3efc27f 100644 --- a/tools/eslint/suppressions.json +++ b/tools/eslint/suppressions.json @@ -1499,41 +1499,6 @@ "count": 1 } }, - "packages/block-editor/src/components/inspector-controls/block-support-slot-container.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/block-support-tools-panel.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/fill.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/index.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/last-item.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/list-view-content-popover.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, - "packages/block-editor/src/components/inspector-controls/slot.jsx": { - "react/jsx-filename-extension": { - "count": 1 - } - }, "packages/block-editor/src/components/inspector-popover-header/index.jsx": { "@wordpress/use-recommended-components": { "count": 3 From dce76e4bcbf72081ea91663de46216e06b986b3b Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 14 Oct 2026 22:55:15 +0530 Subject: [PATCH 3/5] Block Editor: Add the CHANGELOG entry for #84725 --- packages/block-editor/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 158c2790ac943a..ba761d64e80f72 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -48,6 +48,7 @@ - Convert the block support helpers to TypeScript ([#84721](https://github.com/WordPress/gutenberg/pull/84721)). - Convert the layout helpers and block gap utils to TypeScript ([#84722](https://github.com/WordPress/gutenberg/pull/84722)). - Convert the block attribute filter hooks to TypeScript ([#84723](https://github.com/WordPress/gutenberg/pull/84723)). +- Convert `InspectorControls` to TypeScript ([#84725](https://github.com/WordPress/gutenberg/pull/84725)). ## 18.1.0 (2026-10-07) From fd344eb8a7796cd4f13f473c659afa357c43f1b7 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Thu, 15 Oct 2026 20:15:45 +0530 Subject: [PATCH 4/5] Block Editor: Type the block context passed to reset-all filters --- .../components/inspector-controls/types.ts | 11 +--------- packages/block-editor/src/hooks/types.ts | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/inspector-controls/types.ts b/packages/block-editor/src/components/inspector-controls/types.ts index 58366a57746014..754f292769b7a8 100644 --- a/packages/block-editor/src/components/inspector-controls/types.ts +++ b/packages/block-editor/src/components/inspector-controls/types.ts @@ -1,6 +1,6 @@ import type { ElementType, ReactNode, RefObject } from 'react'; import type { createSlotFill } from '@wordpress/components'; -import type { ResetAllFilter } from '../../hooks/types'; +import type { ResetAllContext, ResetAllFilter } from '../../hooks/types'; /** * A slot and fill pair. @@ -45,15 +45,6 @@ export interface BlockSupportSlotContainerProps { [ prop: string ]: unknown; } -/** - * The block a reset-all filter resets. - */ -export interface ResetAllContext { - attributes: Record< string, unknown >; - clientId: string; - name: string | null; -} - /** * A reset-all filter the block support tools panel runs for each block. */ diff --git a/packages/block-editor/src/hooks/types.ts b/packages/block-editor/src/hooks/types.ts index 7116884263bd43..40ad775c9eb3a1 100644 --- a/packages/block-editor/src/hooks/types.ts +++ b/packages/block-editor/src/hooks/types.ts @@ -16,11 +16,24 @@ export interface BlockStyleState { export type BlockStyle = Record< string, unknown >; /** - * A Tools Panel reset-all filter, returning the attributes to set. + * The block a reset-all filter resets. */ -export type ResetAllFilter = ( attributes: { - style?: BlockStyle; -} ) => ( Record< string, unknown > & { style?: BlockStyle } ) | undefined; +export interface ResetAllContext { + attributes: Record< string, unknown >; + clientId: string; + name: string | null; +} + +/** + * A Tools Panel reset-all filter, returning the attributes to set. The block + * support tools panel passes the block context; state-scoped filters do not. + */ +export type ResetAllFilter = ( + attributes: { + style?: BlockStyle; + }, + context?: ResetAllContext +) => ( Record< string, unknown > & { style?: BlockStyle } ) | undefined; /** * Props a block support's controls receive: the block's identity and the From 11d0eafbfa73557dbf3e3eb2c71cb3eb6a5faffa Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Fri, 16 Oct 2026 07:01:13 +0530 Subject: [PATCH 5/5] Block Editor: Keep the InspectorControls slot prop types through forwardRef --- .../src/components/inspector-controls/slot.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/inspector-controls/slot.tsx b/packages/block-editor/src/components/inspector-controls/slot.tsx index e5efd80707b0c3..d9606996a9c33f 100644 --- a/packages/block-editor/src/components/inspector-controls/slot.tsx +++ b/packages/block-editor/src/components/inspector-controls/slot.tsx @@ -1,7 +1,11 @@ import { __experimentalUseSlotFills as useSlotFills } from '@wordpress/components'; import { forwardRef } from '@wordpress/element'; import warning from '@wordpress/warning'; -import type { ForwardedRef } from 'react'; +import type { + ForwardedRef, + ForwardRefExoticComponent, + RefAttributes, +} from 'react'; import BlockSupportToolsPanel from './block-support-tools-panel'; import BlockSupportSlotContainer from './block-support-slot-container'; import groups from './groups'; @@ -52,4 +56,7 @@ function InspectorControlsSlot( ); } -export default forwardRef( InspectorControlsSlot ); +// `forwardRef` drops the named props of a type with an index signature. +export default forwardRef( InspectorControlsSlot ) as ForwardRefExoticComponent< + InspectorControlsSlotProps & RefAttributes< unknown > +>;