We are using Material-UI as the base of a project, but are running in to issues extending components
import { Link, LinkProps } from "@material-ui/core";
export const HnLink = Link;
Works fine
import { Link, LinkProps } from "@material-ui/core";
export const HnLink: React.FC<LinkProps> = props => {
// Some extra functionality
return <Link {...props} />
}
Does not accept component and to props, resulting in TypeScript errors.
Type '{ to: string; component: typeof Link; }' is not assignable to type 'IntrinsicAttributes & AnchorHTMLAttributes<HTMLAnchorElement> & Pick<OverrideProps<TypographyTypeMap<{}, "span">, "span">, "ref" | ... 262 more ... | "variantMapping"> & { ...; } & CommonProps<...> & Pick<...> & { ...; }'.
Property 'to' does not exist on type 'IntrinsicAttributes & AnchorHTMLAttributes<HTMLAnchorElement> & Pick<OverrideProps<TypographyTypeMap<{}, "span">, "span">, "ref" | ... 262 more ... | "variantMapping"> & { ...; } & CommonProps<...> & Pick<...> & { ...; }'.ts(2322)
The same is true of Tabs, wherein wrapping Tabs in our own component causes onChange to not be accepted
Type '(event: ChangeEvent<{}>, value: number) => void' is not assignable to type '((event: ChangeEvent<{}>, value: any) => void) & ((event: FormEvent<HTMLButtonElement>) => void)'.
Type '(event: ChangeEvent<{}>, value: number) => void' is not assignable to type '(event: FormEvent<HTMLButtonElement>) => void'.ts(2322)
As also seen here: https://github.com/mui-org/material-ui/issues/17454
Is there a 'correct' way to wrap a Material-UI component that can alleviate typing issues?
Can you try React.ComponentProps<typeof Link> instead of LinkProps?
@embeddedt That causes the same typescript issue
https://codesandbox.io/s/react-typescript-vir6c
Until that issue is resolved, you can pass along the generic parameters:
import Link, { LinkProps, LinkTypeMap } from "@material-ui/core/Link";
import React from "react";
export const HnLink = <
D extends React.ElementType = LinkTypeMap["defaultComponent"],
P = {}
>(
props: LinkProps<D, P>,
) => {
return <Link {...props} />;
};
export const test = <HnLink to="/test" />;
You can retrieve them by viewing the type of LinkProps:
export type LinkProps<
D extends React.ElementType = LinkTypeMap['defaultComponent'],
P = {}
> = OverrideProps<LinkTypeMap<P, D>, D>;
The shorter example of implementing HnLink can be found in the documentation.
The problem with implementing LinkProps so it can be used without generic component is that TS has difficulties inferring a type from an object property type.
Say we have following fictional type
type T1<C> = {
prop: C;
} & C;
Then we want to use it like
const t: T1 = { prop: { a: 'aaa' }}
We expect TS to infer the generic type C from usage. In our sample C should become { a: 'aaa' }. But TS complains that T1 should have one type argument.
So I don't see a way to implement LInkProps in a way that it infer component prop type from it usage. The only solution I see is described in TS guide I've provided above.
Most helpful comment
Until that issue is resolved, you can pass along the generic parameters:
You can retrieve them by viewing the type of
LinkProps: