Material-ui: Missing typescript attribute for Button component

Created on 23 Aug 2017  路  10Comments  路  Source: mui-org/material-ui

Problem description

I'm using Link component from react-router-4 but the property _to_ is missing from material-ui Button component. I tried to use spread operator but then webpack is giving me error.
When I manually add _to_ inside "material-ui/index.d.ts" then it works.

Steps to reproduce

<Button component={Link} to="/about">About</Button>

Tried this and still not working

<Button component={Link} {...{ to: "/about" }}>About</Button>

Versions

  • Material-UI: 1.0.0-beta.6
  • React: 15.6.1
  • React-Router-Dom: 4.1.2
typescript

Most helpful comment

Ugly but this might work:
<Button component={Link} {...{ to: "/about" } as any} />

All 10 comments

It's unlikely any typing library will be smart enough to cover this use case. (I might be wrong still).

Ugly but this might work:
<Button component={Link} {...{ to: "/about" } as any} />

We can't really fix that other than allow every prop on ButtonBase. But I am not sure how good the autocompletion of IDEs would be for the rest of the props. TS can not infer props allowd on the component passed to [component] AFAIK.

My suggestion is to use the solution proposed by @strothj and wrap it in a LinkButton component.

Just realized that I had webpack errors because I forgot to remove _Button_ with _to_ from on of my testing components. That's why I got errors.

Anyways this example:
<Button component={Link} {...{ to: "/about" }}>About</Button>
is now working. It's not pretty but it's working :)

Could we potentially add the component's type as a generic type parameter for ButtonBase (or use it implicitly), then imply the intersection of the button's and the component's prop types?

Anyways this example:
<Button component={Link} {...{ to: "/about" }}>About</Button>
is now working. It's not pretty but it's working :)

A little prettier approach:

to.tsx

import { LocationDescriptor } from 'history';
import { Link } from 'react-router-dom';

const to = (to: LocationDescriptor) => ({ to, component: Link as any });

export default to;

Then just do this:

import to from './to';

//...
<Button {...to("/page/1")} variant="flat">Click Here!</Button>
//...

Or write your own material button component:

interface ButtonLinkProps extends ButtonProps {
    to: string
}

const ButtonLink = (props: ButtonLinkProps) => <Button {...props} component={({ innerRef, ...linkProps }) => <Link {...linkProps} to={props.to} />} >{props.children}</Button>;

And use this like a normal button:

<ButtonLink variant="outlined" color="primary" to="/" >Home</ButtonLink>

Above pattern is discouraged: See https://material-ui.com/guides/composition/#caveat-with-inlining for a detailed explanation.

You are right!
Again:

import React from "react";
import { Link } from "react-router-dom";
import Button, { ButtonProps } from "@material-ui/core/Button";
import { LocationDescriptor } from "history";

interface ButtonLinkProps extends ButtonProps {
    to: LocationDescriptor
}

export class ButtonLink extends React.Component<ButtonLinkProps>{

    public render() {
        return (
            <Button {...this.props} component={({ innerRef, ...linkProps }) => <Link {...linkProps} to={this.props.to} />} >
                {this.props.children}
            </Button>);
    }
} 

And use this like a normal button:

<ButtonLink variant="outlined" color="primary" to="/" >Home</ButtonLink>

I feel like this is a good use case for @ts-ignore and a new component:

import * as React from 'react';
import { Link } from 'react-router-dom';
import Button, { ButtonProps } from '@material-ui/core/Button';
import { LocationDescriptor } from 'history';

interface ButtonLinkProps extends ButtonProps {
  to: LocationDescriptor;
}

const ButtonLink = ({ to, ...props }: ButtonLinkProps) => {
  // @ts-ignore: see https://github.com/mui-org/material-ui/issues/7877
  return <Button component={Link} to={to} {...props} />;
};

export default ButtonLink;

This solution has no disadvantage:

  • Strict typechecking will still happen.
  • There will be no unexpected unmounting behavior.
  • The code does not look like an awful hack.
  • It does not use anything fancy.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rbozan picture rbozan  路  3Comments

anthony-dandrea picture anthony-dandrea  路  3Comments

finaiized picture finaiized  路  3Comments

ghost picture ghost  路  3Comments

iamzhouyi picture iamzhouyi  路  3Comments