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

Typescript: in ListItem, property button is no more boolean #14971

Closed
2 tasks done
sveyret opened this issue Mar 20, 2019 · 33 comments · Fixed by #26446
Closed
2 tasks done

Typescript: in ListItem, property button is no more boolean #14971

sveyret opened this issue Mar 20, 2019 · 33 comments · Fixed by #26446
Labels
bug 🐛 Something doesn't work component: list This is the name of the generic UI component, not the React module! typescript

Comments

@sveyret
Copy link
Contributor

sveyret commented Mar 20, 2019

The following code in not compiling anymore in mui v4.0:

<ListItem button={editable} >
  <ListItemText primary="text" />
</ListItem>

where editable is a boolean value.

  • This is not a v0.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior 🤔

Should compile without error.

Current Behavior 😯

Type 'boolean' is not assignable to type 'true'.

button={editable}

../../../node_modules/@material-ui/core/ListItem/ListItem.d.ts:23:38
23 ExtendButtonBase<ListItemTypeMap<{ button: true }, 'div'>>;

The expected type comes from property 'button' which is declared here on type 'IntrinsicAttributes & { action?: ((actions: ButtonBaseActions) => void) | undefined; buttonRef?: ((instance: any) => void) | RefObject | null | undefined; centerRipple?: boolean | undefined; ... 6 more ...; TouchRippleProps?: Partial<...> | undefined; } & { ...; } & { ...; } & CommonProps<...> & Pick<...>'

Your Environment 🌎

Tech Version
Material-UI 4.0.0-alpha.4
React 16.8.4
@types/react 16.8.8
TypeScript 3.3.3333
@oliviertassinari oliviertassinari added status: waiting for author Issue with insufficient information typescript labels Mar 20, 2019
@oliviertassinari
Copy link
Member

I fail to reproduce the problem. Do you have a reproduction?

@sveyret
Copy link
Contributor Author

sveyret commented Mar 21, 2019

Thank you @oliviertassinari for looking at it.

I cannot reproduce on CodeSandbox, even if I use the same configuration as home. 😥

I made a minimal project where the compilation fails because of this issue (see its README in order to see how to use it):
https://github.com/sveyret/issue-mui-14971

@oliviertassinari oliviertassinari removed the status: waiting for author Issue with insufficient information label Mar 21, 2019
@eps1lon
Copy link
Member

eps1lon commented Mar 25, 2019

@sveyret Please add a lockfile to your repository. Otherwise changed dependencies over time might change to result.

@eps1lon eps1lon added bug 🐛 Something doesn't work component: ListItem labels Mar 25, 2019
@eps1lon eps1lon added this to the v4 milestone Mar 25, 2019
@sveyret
Copy link
Contributor Author

sveyret commented Mar 25, 2019

@eps1lon Done.

@eps1lon
Copy link
Member

eps1lon commented Mar 25, 2019

@sveyret Looks like a limitation of TypeScript. For more details see #15049. Will keep an eye on this one but you might have to use an any cast

@sveyret
Copy link
Contributor Author

sveyret commented Mar 25, 2019

Thank you @eps1lon, I hadn't notice it became a discriminent. I'll update my code for that.

@eps1lon
Copy link
Member

eps1lon commented Mar 27, 2019

We have added a test which includes a workaround (with runtime overhead) for this issue. If anybody can come up with a solution that breaks the test in #15049 but passes all other tests we would be happy to accept PRs with that patch.

@leoyli
Copy link

leoyli commented May 28, 2019

I have the same issue with MeunItem component...

@oliviertassinari oliviertassinari removed this from the v4 milestone Jun 7, 2019
@le0nik

This comment has been minimized.

@eps1lon

This comment has been minimized.

@le0nik

This comment has been minimized.

@eps1lon

This comment has been minimized.

@le0nik

This comment has been minimized.

@ffjanhoeck

This comment has been minimized.

@rosskevin
Copy link
Member

rosskevin commented Jun 20, 2019

/related #16245

Reproduction/broken test pr #16315

@cucco-io

This comment has been minimized.

@oliviertassinari oliviertassinari added component: list This is the name of the generic UI component, not the React module! and removed component: ListItem labels Aug 20, 2019
@eps1lon
Copy link
Member

eps1lon commented Aug 28, 2019

In case it got drowned out: The latest status is #14971 (comment).

@franklixuefei
Copy link
Contributor

franklixuefei commented Oct 9, 2019

Probably a silly question -
From the type definition of ListItem, I see this:

declare const ListItem: OverridableComponent<ListItemTypeMap<{ button?: false }, 'li'>> &
  ExtendButtonBase<ListItemTypeMap<{ button: true }, 'div'>>;

I wonder why we are explicitly putting false here as the type?

@eps1lon
Copy link
Member

eps1lon commented Oct 10, 2019

I wonder why we are explicitly putting false here as the type?

  • false | undefined is the discriminator for when a ListItem does not use Button
  • true for when it does use a Button

See http://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions

@lcswillems

This comment has been minimized.

@oliviertassinari oliviertassinari changed the title [Material-ui 4] Typescript: in ListItem, property button is no more boolean Typescript: in ListItem, property button is no more boolean Dec 1, 2019
@szilarddoro
Copy link

In my case, this particular issue causes errors with ListItem's "component" prop as well. If I manually edit the { button: true } to { button: boolean } and { button?: false } to { button?: boolean }, then the "component" prop is getting suddenly available on ListItem.

@ErwanDL
Copy link

ErwanDL commented Jan 7, 2020

Having the same issue, I'm trying to do

    const unlocked = status !== "locked";
    return(<ListItem button={unlocked} />)

and I get the error message Type 'boolean' is not assignable to type 'true'..

Seems pretty counter-intuitive to me

@amosbastian
Copy link

amosbastian commented Jan 20, 2020

Getting a similar problem when using styled-components like so:

const StyledListItem = styled(ListItem)`
    &:hover {
        background-color: rgba(58, 189, 159, 0.15);
    }
`;

which gives me the error No overload matches this call. and says ListItem.d.ts(24, 38): 'button' is declared here.. No error if I use it like <StyledListItem button>, but without button it does give an error. Idk if this is expected behaviour or not.

@vicentelyrio
Copy link

Having the same issue, I'm trying to do

    const unlocked = status !== "locked";
    return(<ListItem button={unlocked} />)

and I get the error message Type 'boolean' is not assignable to type 'true'..

Seems pretty counter-intuitive to me

@ErwanDL For me a cast worked:

return(<ListItem button={unlocked as true | undefined} />)
```

@sveyret
Copy link
Contributor Author

sveyret commented Feb 3, 2020

Can't the evolution provided by TS 3.5 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#smarter-union-type-checking provide a way to correct this?

@eps1lon
Copy link
Member

eps1lon commented Feb 3, 2020

Can't the evolution provided by TS 3.5 typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#smarter-union-type-checking provide a way to correct this?

Since we rely on function overloads the feature in 3.5 does not help us. It only seems to apply to object property checking.

@cloleb
Copy link

cloleb commented Apr 20, 2020

The following "solution" doesn't fix the root problem with the TS MUI types but provides a way to temporarily overcome the typescript errors.

Declare a custom interface:
interface CustomMenuItemProps extends MuiMenuItemProps { button?: any; }

Create a new component wrapping the original material-ui component and pass in the TS interface:
<MuiMenuItem {...CustomMenuItemProps} ref={ref} > {children} </MuiMenuItem>

@Kizmar
Copy link

Kizmar commented Jun 9, 2020

Landed here after trying to calculate if a ListItem should be a button based on props. After seeing the same thing @franklixuefei talked about, I wasn't sure how we're supposed to toggle this property.

I'd like to be able to do something like <ListItem button={!isDisabled}>.... Is the only workaround at this point to wrap the component as @ClovisLeb mentioned?

[Edit] Of course as soon as I typed this up I thought, "why not just make your bool 'any'?" That works. So now I'm using this to toggle the button:
const isButton: any = isDisabled || isLocked ? false : true;
<ListItem button={isButton} ...

@wrsx
Copy link

wrsx commented Aug 18, 2020

As mentioned above it's just a downside of using boolean literals as a discriminator. It's necessary to conditionally show what props are available.

Either be explicit:

if (showButton) {
  return <ListItem button={true} />;
} else {
  return <ListItem button={false} />;
}

Or cast to any:

<ListItem button={showButton as any} />

@AhmedBHameed
Copy link

Looks like a limitation of TypeScript. For more details see #15049. Will keep an eye on this one but you might have to use an any cast

@eps1lon thank you for pointing me to the same issue.

IMHO, casting with any might be not a nice solution due the following reason:

  • In typescript with eslint, If eslint allowing any in the following rule '@typescript-eslint/no-explicit-any': 'off', it can make someone produce/forgetting any types anywhere in the code. It is very nice to prevent using any because it can open a question of what is type shape this variable could be (Especially if the name of the variable is not meaningful enough).

Typescript sometimes makes me scratch my head as this issue does to me. I was thinking about using enum but not sure if it nice to make user import the enum in his code in order to use it. But on the other side, we don't need if else also (I might be wrong tho).

enum ClassOfBook {
  YES = 'yes',
  NO = 'no'
}

interface Book {
  isBook: ClassOfBook
}

interface OverloadedComponent {
  (props: Book): void;
}

declare const SomeComponent: OverloadedComponent;

function Library(props: Book) {
  const { isBook } = props;
  SomeComponent(props);

  SomeComponent({ isBook: ClassOfBook.YES });
}

@akharkhonov
Copy link
Contributor

Furthermore, ts can't compile styled ListItem with button prop that is set to inline false
https://codesandbox.io/s/damp-grass-3w9vs?file=/src/App.tsx

@JanCizmar
Copy link

If you are looking for workaround, something like this works:

type PropTypes = Omit<ComponentProps<typeof ListItem>, "button"> & {button?: boolean}

export const SimpleListItem: FunctionComponent<PropTypes> = (props) => {
    const classes = useStyles();

    return (
            <ListItem {...props} button={props.button as any} classes={{container: classes.container}}>
                {props.children}
            </ListItem>
    );
}

@siriwatknp
Copy link
Member

I think this issue can be closed once #26446 merged.

Summary

<ListItem button> is deprecated, use ListItemButton instead.

// before
<ListItem button>

// v5
<ListItemButton>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: list This is the name of the generic UI component, not the React module! typescript
Projects
None yet
Development

Successfully merging a pull request may close this issue.