Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Try alternative to OverrideComponent type (ver 2) #33964

Closed
2 changes: 2 additions & 0 deletions docs/src/modules/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(function Link(props,
const nextjsProps = { to: href, linkAs, replace, scroll, shallow, prefetch, locale };

if (noLinkStyle) {
// @ts-ignore TODO: this needs to be resolved
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that in both implementations these ts-ignores are required.

return <NextLinkComposed className={className} ref={ref} {...nextjsProps} {...other} />;
}

return (
// @ts-ignore TODO: this needs to be resolved
<MuiLink
component={NextLinkComposed}
className={className}
Expand Down
3 changes: 1 addition & 2 deletions packages/mui-material/src/Link/Link.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import { expectType } from '@mui/types';

<Link
component="button"
// @ts-expect-error Implicit any
ref={(elem) => {
expectType<any, typeof elem>(elem);
expectType<HTMLButtonElement | null, typeof elem>(elem);
}}
>
Home
Expand Down
25 changes: 14 additions & 11 deletions packages/mui-material/src/OverridableComponent.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { DistributiveOmit } from '@mui/types';
import { Merge } from '@mui/types';
import { StyledComponentProps } from './styles';

/**
Expand All @@ -13,13 +13,15 @@ export interface OverridableComponent<M extends OverridableTypeMap> {
// Also, there are types in MUI Base that have a similar shape to this interface
// (e.g. SelectUnstyledType, OptionUnstyledType, etc.).
<C extends React.ElementType>(
props: {
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: C;
} & OverrideProps<M, C>,
props: C extends ''
? { component: keyof JSX.IntrinsicElements }
: C extends React.ComponentClass<infer P>
? Merge<P, BaseProps<M> & { component: C; ref?: React.Ref<InstanceType<C>> }>
: C extends React.ComponentType<infer P>
? Merge<P, BaseProps<M> & { component: C }>
: C extends keyof JSX.IntrinsicElements
? Merge<JSX.IntrinsicElements[C], BaseProps<M> & { component: C }>
: never,
): JSX.Element;
(props: DefaultComponentProps<M>): JSX.Element;
}
Expand All @@ -33,16 +35,17 @@ export type OverrideProps<
C extends React.ElementType
> = (
& BaseProps<M>
& DistributiveOmit<React.ComponentPropsWithRef<C>, keyof BaseProps<M>>
& Omit<React.ComponentProps<C>, keyof BaseProps<M>>
);

/**
* Props if `component={Component}` is NOT used.
*/
// prettier-ignore
export type DefaultComponentProps<M extends OverridableTypeMap> =
export type DefaultComponentProps<M extends OverridableTypeMap> = (
& BaseProps<M>
& DistributiveOmit<React.ComponentPropsWithRef<M['defaultComponent']>, keyof BaseProps<M>>;
& Omit<React.ComponentProps<M['defaultComponent']>, keyof BaseProps<M>>
);

/**
* Props defined on the component (+ common material-ui props).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface MyOverrideProps {
myString?: string;
myCallback?(n: number): void;
}
declare const MyOverrideComponent: React.ComponentType<MyOverrideProps>;
declare const MyOverrideComponent: React.FunctionComponent<MyOverrideProps>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be double checked, for some reason I needed to change the type from ComponentType to FunctionComponent.

class MyOverrideClassComponent extends React.Component<MyOverrideProps> {
render() {
return null;
Expand All @@ -16,7 +16,7 @@ class MyOverrideClassComponent extends React.Component<MyOverrideProps> {
const MyOverrideRefForwardingComponent = React.forwardRef<HTMLLegendElement>((props, ref) => (
<div ref={ref} />
));
declare const MyIncompatibleComponent1: React.ComponentType<{ inconsistentProp?: number }>;
declare const MyIncompatibleComponent1: React.FunctionComponent<{ inconsistentProp?: number }>;

declare const Foo: OverridableComponent<{
props: {
Expand Down
22 changes: 15 additions & 7 deletions packages/mui-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ export type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 :
*/
export function expectType<Expected, Actual>(actual: IfEquals<Actual, Expected, Actual>): void;

/* -------------------------------------------------------------------------------------------------
* Utility types
* -----------------------------------------------------------------------------------------------*/
export type Merge<P1 = {}, P2 = {}> = Omit<P1, keyof P2> & P2;

/**
* A component whose root component can be controlled via a `component` prop.
*
Expand All @@ -97,18 +102,21 @@ export interface OverridableComponent<M extends OverridableTypeMap> {
// Also, there are types in MUI Base that have a similar shape to this interface
// (e.g. SelectUnstyledType, OptionUnstyledType, etc.).
<C extends React.ElementType>(
props: {
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: C;
} & OverrideProps<M, C>,
props: C extends ''
? { component: keyof JSX.IntrinsicElements }
: C extends React.ComponentClass<infer P>
? Merge<P, BaseProps<M> & { component: C; ref?: React.Ref<InstanceType<C>> }>
: C extends React.ComponentType<infer P>
? Merge<P, BaseProps<M> & { component: C }>
: C extends keyof JSX.IntrinsicElements
? Merge<JSX.IntrinsicElements[C], BaseProps<M> & { component: C }>
: never,
): JSX.Element;
(props: DefaultComponentProps<M>): JSX.Element;
propTypes?: any;
}

// TODO: Update the OverrideProps type
/**
* Props of the component if `component={Component}` is used.
*/
Expand Down