type: optimize CompoundedComponent type (#48346)

* type: optimize CompoundedComponent type

* chore: fix

* type: fix type

* chore: fix

* type: fix type

* type: add ts-ignore

* fix: fix

* chore: revert
This commit is contained in:
lijianan 2024-04-09 16:49:47 +08:00 committed by GitHub
parent 44cd5342f7
commit 854cdb4352
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 124 additions and 164 deletions

View File

@ -1,6 +1,3 @@
import type { ForwardRefExoticComponent, RefAttributes } from 'react';
import type { AvatarProps } from './avatar';
import InternalAvatar from './avatar';
import Group from './group';
@ -8,9 +5,7 @@ export type { AvatarProps } from './avatar';
export type { GroupProps } from './group';
export { Group };
type CompoundedComponent = ForwardRefExoticComponent<
AvatarProps & RefAttributes<HTMLSpanElement>
> & {
type CompoundedComponent = typeof InternalAvatar & {
Group: typeof Group;
};

View File

@ -2,6 +2,7 @@ import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import type { GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Tooltip from '../../tooltip';
@ -75,7 +76,7 @@ describe('Badge', () => {
// https://github.com/ant-design/ant-design/issues/10626
it('should be composable with Tooltip', () => {
const ref = React.createRef<typeof Tooltip>();
const ref = React.createRef<GetRef<typeof Tooltip>>();
const { container } = render(
<Tooltip title="Fix the error" ref={ref}>
<Badge status="error" />

View File

@ -15,12 +15,6 @@ import useStyle from './style';
export type { ScrollNumberProps } from './ScrollNumber';
type CompoundedComponent = React.ForwardRefExoticComponent<
BadgeProps & React.RefAttributes<HTMLSpanElement>
> & {
Ribbon: typeof Ribbon;
};
export interface BadgeProps {
/** Number to show in badge */
count?: React.ReactNode;
@ -51,7 +45,7 @@ export interface BadgeProps {
};
}
const InternalBadge: React.ForwardRefRenderFunction<HTMLSpanElement, BadgeProps> = (props, ref) => {
const InternalBadge = React.forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
@ -271,9 +265,13 @@ const InternalBadge: React.ForwardRefRenderFunction<HTMLSpanElement, BadgeProps>
{statusTextNode}
</span>,
);
});
type CompoundedComponent = typeof InternalBadge & {
Ribbon: typeof Ribbon;
};
const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(InternalBadge) as CompoundedComponent;
const Badge = InternalBadge as CompoundedComponent;
Badge.Ribbon = Ribbon;

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import { ConfigContext } from '../config-provider';
type CompoundedComponent = React.FC<{ children?: React.ReactNode }> & {
type CompoundedComponent = React.FC<React.PropsWithChildren> & {
/** @internal */
__ANT_BREADCRUMB_SEPARATOR: boolean;
};
@ -10,7 +10,6 @@ type CompoundedComponent = React.FC<{ children?: React.ReactNode }> & {
const BreadcrumbSeparator: CompoundedComponent = ({ children }) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('breadcrumb');
return (
<li className={`${prefixCls}-separator`} aria-hidden="true">
{children === '' ? children : children || '/'}

View File

@ -4,6 +4,7 @@ import { resetWarned } from 'rc-util/lib/warning';
import { act } from 'react-dom/test-utils';
import Button from '..';
import type { GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
@ -292,7 +293,7 @@ describe('Button', () => {
});
it('skip check 2 words when ConfigProvider disable this', () => {
const buttonInstance = React.createRef<HTMLElement>();
const buttonInstance = React.createRef<GetRef<typeof Button>>();
render(
<ConfigProvider autoInsertSpaceInButton={false}>
<Button ref={buttonInstance}>test</Button>
@ -369,13 +370,13 @@ describe('Button', () => {
/>
);
const btnRef = React.createRef<HTMLButtonElement>();
const btnRef = React.createRef<GetRef<typeof Button>>();
const refBtn = <Button ref={btnRef} />;
const anchorRef = React.createRef<HTMLAnchorElement>();
const anchorRef = React.createRef<GetRef<typeof Button>>();
const refAnchor = <Button ref={anchorRef} />;
const htmlRef = React.createRef<HTMLElement>();
const htmlRef = React.createRef<GetRef<typeof Button>>();
const refHtml = <Button ref={htmlRef} />;
const btnAttr = <Button name="hello" />;

View File

@ -1,13 +1,5 @@
/* eslint-disable react/button-has-type */
import React, {
Children,
createRef,
forwardRef,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import React, { Children, createRef, useContext, useEffect, useMemo, useState } from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { composeRef } from 'rc-util/lib/ref';
@ -60,14 +52,6 @@ export interface ButtonProps extends BaseButtonProps, MergedHTMLAttributes {
htmlType?: ButtonHTMLType;
}
type CompoundedComponent = React.ForwardRefExoticComponent<
ButtonProps & React.RefAttributes<HTMLElement>
> & {
Group: typeof Group;
/** @internal */
__ANT_BUTTON: boolean;
};
type LoadingConfigType = {
loading: boolean;
delay: number;
@ -89,10 +73,10 @@ function getLoadingConfig(loading: BaseButtonProps['loading']): LoadingConfigTyp
};
}
const InternalButton: React.ForwardRefRenderFunction<
const InternalCompoundedButton = React.forwardRef<
HTMLButtonElement | HTMLAnchorElement,
ButtonProps
> = (props, ref) => {
>((props, ref) => {
const {
loading = false,
prefixCls: customizePrefixCls,
@ -303,19 +287,22 @@ const InternalButton: React.ForwardRefRenderFunction<
</Wave>
);
}
return wrapCSSVar(buttonNode);
});
type CompoundedComponent = typeof InternalCompoundedButton & {
Group: typeof Group;
/** @internal */
__ANT_BUTTON: boolean;
};
const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(
InternalButton,
) as CompoundedComponent;
const Button = InternalCompoundedButton as CompoundedComponent;
Button.Group = Group;
Button.__ANT_BUTTON = true;
if (process.env.NODE_ENV !== 'production') {
Button.displayName = 'Button';
}
Button.Group = Group;
Button.__ANT_BUTTON = true;
export default Button;

View File

@ -1,7 +1,5 @@
import type * as React from 'react';
import type { CheckboxRef } from 'rc-checkbox';
import type { CheckboxProps } from './Checkbox';
import InternalCheckbox from './Checkbox';
import Group from './Group';
@ -9,9 +7,7 @@ export type { CheckboxChangeEvent, CheckboxProps } from './Checkbox';
export type { CheckboxGroupProps, CheckboxOptionType } from './Group';
export type { CheckboxRef };
type CompoundedComponent = React.ForwardRefExoticComponent<
CheckboxProps & React.RefAttributes<CheckboxRef>
> & {
type CompoundedComponent = typeof InternalCheckbox & {
Group: typeof Group;
/** @internal */
__ANT_CHECKBOX: boolean;

View File

@ -8,21 +8,23 @@ import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import Tooltip from '../tooltip';
import type BackTop from './BackTop';
import FloatButtonGroupContext from './context';
import Content from './FloatButtonContent';
import type FloatButtonGroup from './FloatButtonGroup';
import type {
CompoundedComponent,
FloatButtonBadgeProps,
FloatButtonContentProps,
FloatButtonElement,
FloatButtonProps,
FloatButtonShape,
} from './interface';
import type PurePanel from './PurePanel';
import useStyle from './style';
export const floatButtonPrefixCls = 'float-btn';
const FloatButton = React.forwardRef<FloatButtonElement, FloatButtonProps>((props, ref) => {
const InternalFloatButton = React.forwardRef<FloatButtonElement, FloatButtonProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
@ -107,7 +109,15 @@ const FloatButton = React.forwardRef<FloatButtonElement, FloatButtonProps>((prop
</button>
),
);
}) as CompoundedComponent;
});
type CompoundedComponent = typeof InternalFloatButton & {
Group: typeof FloatButtonGroup;
BackTop: typeof BackTop;
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
};
const FloatButton = InternalFloatButton as CompoundedComponent;
if (process.env.NODE_ENV !== 'production') {
FloatButton.displayName = 'FloatButton';

View File

@ -2,9 +2,6 @@ import type React from 'react';
import type { BadgeProps } from '../badge';
import type { TooltipProps } from '../tooltip';
import type BackTop from './BackTop';
import type Group from './FloatButtonGroup';
import type PurePanel from './PurePanel';
export type FloatButtonElement = HTMLAnchorElement & HTMLButtonElement;
@ -67,11 +64,3 @@ export interface BackTopProps extends Omit<FloatButtonProps, 'target'> {
style?: React.CSSProperties;
duration?: number;
}
export type CompoundedComponent = React.ForwardRefExoticComponent<
FloatButtonProps & React.RefAttributes<FloatButtonElement>
> & {
Group: typeof Group;
BackTop: typeof BackTop;
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
};

View File

@ -1,7 +1,4 @@
import type * as React from 'react';
import Group from './Group';
import type { InputProps, InputRef } from './Input';
import InternalInput from './Input';
import OTP from './OTP';
import Password from './Password';
@ -14,9 +11,7 @@ export type { PasswordProps } from './Password';
export type { SearchProps } from './Search';
export type { TextAreaProps } from './TextArea';
type CompoundedComponent = React.ForwardRefExoticComponent<
InputProps & React.RefAttributes<InputRef>
> & {
type CompoundedComponent = typeof InternalInput & {
Group: typeof Group;
Search: typeof Search;
TextArea: typeof TextArea;

View File

@ -1,13 +1,5 @@
import type {
CSSProperties,
FC,
ForwardRefExoticComponent,
ForwardRefRenderFunction,
HTMLAttributes,
ReactElement,
ReactNode,
} from 'react';
import React, { Children, forwardRef, useContext } from 'react';
import type { CSSProperties, FC, HTMLAttributes, ReactElement, ReactNode } from 'react';
import React, { Children, useContext } from 'react';
import classNames from 'classnames';
import { cloneElement } from '../_util/reactNode';
@ -63,15 +55,16 @@ export const Meta: FC<ListItemMetaProps> = ({
);
};
export interface ListItemTypeProps
extends ForwardRefExoticComponent<ListItemProps & React.RefAttributes<HTMLElement>> {
Meta: typeof Meta;
}
const InternalItem: ForwardRefRenderFunction<HTMLDivElement, ListItemProps> = (
{ prefixCls: customizePrefixCls, children, actions, extra, className, colStyle, ...others },
ref,
) => {
const InternalItem = React.forwardRef<HTMLDivElement, ListItemProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
children,
actions,
extra,
className,
colStyle,
...others
} = props;
const { grid, itemLayout } = useContext(ListContext);
const { getPrefixCls } = useContext(ConfigContext);
@ -130,7 +123,6 @@ const InternalItem: ForwardRefRenderFunction<HTMLDivElement, ListItemProps> = (
: [children, actionsContent, cloneElement(extra, { key: 'extra' })]}
</Element>
);
return grid ? (
<Col ref={ref} flex={1} style={colStyle}>
{itemChildren}
@ -138,8 +130,13 @@ const InternalItem: ForwardRefRenderFunction<HTMLDivElement, ListItemProps> = (
) : (
itemChildren
);
});
export type ListItemTypeProps = typeof InternalItem & {
Meta: typeof Meta;
};
const Item = forwardRef(InternalItem) as ListItemTypeProps;
const Item = InternalItem as ListItemTypeProps;
Item.Meta = Meta;

View File

@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import List from '..';
import type { GetRef } from '../../_util/type';
import { pureRender, render } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
@ -189,15 +190,13 @@ describe('List Item Layout', () => {
});
it('should ref', () => {
const ref = React.createRef<HTMLElement>();
const ref = React.createRef<GetRef<typeof List.Item>>();
render(<List.Item ref={ref}>Item</List.Item>);
expect(ref.current).toHaveClass('ant-list-item');
});
it('should grid ref', () => {
const ref = React.createRef<HTMLElement>();
const ref = React.createRef<GetRef<typeof List.Item>>();
render(
<List grid={{}}>
<List.Item ref={ref}>Item</List.Item>,

View File

@ -64,18 +64,7 @@ interface MentionsEntity {
value: string;
}
type CompoundedComponent = React.ForwardRefExoticComponent<
MentionProps & React.RefAttributes<MentionsRef>
> & {
Option: typeof Option;
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
getMentions: (value: string, config?: MentionsConfig) => MentionsEntity[];
};
const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps> = (
props,
ref,
) => {
const InternalMentions = React.forwardRef<MentionsRef, MentionProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
@ -223,14 +212,20 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
);
return wrapCSSVar(mentions);
});
type CompoundedComponent = typeof InternalMentions & {
Option: typeof Option;
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
getMentions: (value: string, config?: MentionsConfig) => MentionsEntity[];
};
const Mentions = React.forwardRef<MentionsRef, MentionProps>(
InternalMentions,
) as CompoundedComponent;
const Mentions = InternalMentions as CompoundedComponent;
if (process.env.NODE_ENV !== 'production') {
Mentions.displayName = 'Mentions';
}
Mentions.Option = Option;
// We don't care debug panel

View File

@ -37,7 +37,7 @@ export interface PopconfirmState {
open?: boolean;
}
const Popconfirm = React.forwardRef<TooltipRef, PopconfirmProps>((props, ref) => {
const InternalPopconfirm = React.forwardRef<TooltipRef, PopconfirmProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
placement = 'top',
@ -112,12 +112,14 @@ const Popconfirm = React.forwardRef<TooltipRef, PopconfirmProps>((props, ref) =>
{children}
</Popover>,
);
}) as React.ForwardRefExoticComponent<
React.PropsWithoutRef<PopconfirmProps> & React.RefAttributes<unknown>
> & {
});
type CompoundedComponent = typeof InternalPopconfirm & {
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
};
const Popconfirm = InternalPopconfirm as CompoundedComponent;
// We don't care debug panel
/* istanbul ignore next */
Popconfirm._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;

View File

@ -36,7 +36,7 @@ const Overlay: React.FC<OverlayProps> = ({ title, content, prefixCls }) => (
</>
);
const Popover = React.forwardRef<TooltipRef, PopoverProps>((props, ref) => {
const InternalPopover = React.forwardRef<TooltipRef, PopoverProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
title,
@ -109,16 +109,18 @@ const Popover = React.forwardRef<TooltipRef, PopoverProps>((props, ref) => {
})}
</Tooltip>,
);
}) as React.ForwardRefExoticComponent<
React.PropsWithoutRef<PopoverProps> & React.RefAttributes<unknown>
> & {
});
type CompoundedComponent = typeof InternalPopover & {
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
};
const Popover = InternalPopover as CompoundedComponent;
Popover._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
if (process.env.NODE_ENV !== 'production') {
Popover.displayName = 'Popover';
}
Popover._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
export default Popover;

View File

@ -1,7 +1,4 @@
import type * as React from 'react';
import Group from './group';
import type { RadioProps, RadioRef } from './interface';
import InternalRadio from './radio';
import Button from './radioButton';
@ -17,9 +14,7 @@ export {
} from './interface';
export { Button, Group };
type CompoundedComponent = React.ForwardRefExoticComponent<
RadioProps & React.RefAttributes<RadioRef>
> & {
type CompoundedComponent = typeof InternalRadio & {
Group: typeof Group;
Button: typeof Button;
/** @internal */

View File

@ -30,7 +30,7 @@ export interface SpaceProps extends React.HTMLAttributes<HTMLDivElement> {
styles?: { item: React.CSSProperties };
}
const Space = React.forwardRef<HTMLDivElement, SpaceProps>((props, ref) => {
const InternalSpace = React.forwardRef<HTMLDivElement, SpaceProps>((props, ref) => {
const { getPrefixCls, space, direction: directionConfig } = React.useContext(ConfigContext);
const {
@ -141,18 +141,16 @@ const Space = React.forwardRef<HTMLDivElement, SpaceProps>((props, ref) => {
);
});
type CompoundedComponent = typeof InternalSpace & {
Compact: typeof Compact;
};
const Space = InternalSpace as CompoundedComponent;
Space.Compact = Compact;
if (process.env.NODE_ENV !== 'production') {
Space.displayName = 'Space';
}
type CompoundedComponent = React.ForwardRefExoticComponent<
SpaceProps & React.RefAttributes<HTMLDivElement>
> & {
Compact: typeof Compact;
};
const CompoundedSpace = Space as CompoundedComponent;
CompoundedSpace.Compact = Compact;
export default CompoundedSpace;
export default Space;

View File

@ -47,14 +47,7 @@ export interface SwitchProps {
id?: string;
}
type CompoundedComponent = React.ForwardRefExoticComponent<
SwitchProps & React.RefAttributes<HTMLElement>
> & {
/** @internal */
__ANT_SWITCH: boolean;
};
const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
const InternalSwitch = React.forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
size: customizeSize,
@ -131,7 +124,14 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>((props, ref) =>
/>
</Wave>,
);
}) as CompoundedComponent;
});
type CompoundedComponent = typeof InternalSwitch & {
/** @internal */
__ANT_SWITCH: boolean;
};
const Switch = InternalSwitch as CompoundedComponent;
Switch.__ANT_SWITCH = true;

View File

@ -33,12 +33,7 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
bordered?: boolean;
}
export interface TagType
extends React.ForwardRefExoticComponent<TagProps & React.RefAttributes<HTMLElement>> {
CheckableTag: typeof CheckableTag;
}
const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (tagProps, ref) => {
const InternalTag = React.forwardRef<HTMLSpanElement, TagProps>((tagProps, ref) => {
const {
prefixCls: customizePrefixCls,
className,
@ -153,9 +148,13 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
);
return wrapCSSVar(isNeedWave ? <Wave component="Tag">{tagNode}</Wave> : tagNode);
});
export type TagType = typeof InternalTag & {
CheckableTag: typeof CheckableTag;
};
const Tag = React.forwardRef<HTMLSpanElement, TagProps>(InternalTag) as TagType;
const Tag = InternalTag as TagType;
if (process.env.NODE_ENV !== 'production') {
Tag.displayName = 'Tag';

View File

@ -123,7 +123,7 @@ export interface TooltipPropsWithTitle extends AbstractTooltipProps {
export declare type TooltipProps = TooltipPropsWithTitle | TooltipPropsWithOverlay;
const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
const InternalTooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
openClassName,
@ -331,12 +331,14 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
return wrapCSSVar(
<zIndexContext.Provider value={contextZIndex}>{content}</zIndexContext.Provider>,
);
}) as React.ForwardRefExoticComponent<
React.PropsWithoutRef<TooltipProps> & React.RefAttributes<unknown>
> & {
});
type CompoundedComponent = typeof InternalTooltip & {
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
};
const Tooltip = InternalTooltip as CompoundedComponent;
if (process.env.NODE_ENV !== 'production') {
Tooltip.displayName = 'Tooltip';
}

View File

@ -1,12 +1,12 @@
import React, { useRef, useState } from 'react';
import { EllipsisOutlined } from '@ant-design/icons';
import type { TourProps } from 'antd';
import type { GetRef, TourProps } from 'antd';
import { Button, Divider, Space, Tour } from 'antd';
const App: React.FC = () => {
const ref1 = useRef<HTMLButtonElement>(null);
const ref2 = useRef<HTMLButtonElement>(null);
const ref3 = useRef<HTMLButtonElement>(null);
const ref1 = useRef<GetRef<typeof Button>>(null);
const ref2 = useRef<GetRef<typeof Button>>(null);
const ref3 = useRef<GetRef<typeof Button>>(null);
const [open, setOpen] = useState<boolean>(false);