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

[Tabs] Issues with onChange TypeScript types #17454

Closed
2 tasks done
ianschmitz opened this issue Sep 16, 2019 · 25 comments · Fixed by #21552
Closed
2 tasks done

[Tabs] Issues with onChange TypeScript types #17454

ianschmitz opened this issue Sep 16, 2019 · 25 comments · Fixed by #21552
Labels
component: tabs This is the name of the generic UI component, not the React module! typescript

Comments

@ianschmitz
Copy link
Contributor

ianschmitz commented Sep 16, 2019

  • The issue is present in the latest release.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Current Behavior 😯

Wrapping the Tabs component results in TypeScript emitting the wrong types for the onChange prop:

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)

The same problem looks to be appearing for action. It appears to be intersecting the types from ButtonBases props. Maybe a union type was intended?

(((instance: TabsActions | null) => void) & ((instance: ButtonBaseActions | null) => void)) | (((instance: TabsActions | null) => void) & React.RefObject<ButtonBaseActions>) | (React.RefObject<...> & ((instance: ButtonBaseActions | null) => void)) | (React.RefObject<...> & React.RefObject<...>) | null | undefined

Expected Behavior 🤔

The type of TabsProps.onChange should be ((event: React.ChangeEvent<{}>, value: any) => void) | undefined. I believe this regression may have been caused by #16487.

Steps to Reproduce 🕹

See https://codesandbox.io/s/upbeat-grass-gc0i9 for the TypeScript error and example wrapping of Tabs component.

Context 🔦

It's quite common for us to wrap up the MUI components with our own customizations and export them as common components to use throughout our applications. In this case after upgrading MUI's minor version recently this started causing TypeScript errors everywhere we've used Tabs.

Your Environment 🌎

Tech Version
Material-UI v4.4.2
React v16.9.0
Browser
TypeScript v3.6.3
etc.
@eps1lon eps1lon added component: tabs This is the name of the generic UI component, not the React module! typescript labels Sep 17, 2019
@ianschmitz
Copy link
Contributor Author

ianschmitz commented Sep 19, 2019

I think there's actually a lot more going on across many components. I'm encountering the same issue in a number of places. For example using Link:

function onClick(event: React.MouseEvent<HTMLButtonElement>) {}

return (<Link component="button" onClick={onClick}>
  Hey!
</Link>);

has error:

Type '(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void' is not assignable to type '((event: MouseEvent<HTMLAnchorElement, MouseEvent>) => void) & ((event: MouseEvent<HTMLElement, MouseEvent>) => void)'.
  Type '(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void' is not assignable to type '(event: MouseEvent<HTMLAnchorElement, MouseEvent>) => void'.
    Types of parameters 'event' and 'event' are incompatible.

Sample: https://codesandbox.io/s/create-react-app-with-typescript-ztfq4

/cc @ypresto

@ianschmitz ianschmitz changed the title [Tabs] Wrapping component results in incorrect TypeScript types [TypeScript] Issues with TypeScript types Oct 3, 2019
@ianschmitz
Copy link
Contributor Author

Updated title to reflect that the issue isn't just within Tabs.

@oliviertassinari oliviertassinari changed the title [TypeScript] Issues with TypeScript types [Tabs] Issues with onChange TypeScript types Dec 1, 2019
@oliviertassinari
Copy link
Member

@ianschmitz I can't reproduce the issue on your codesandbox. Can you?

@ianschmitz
Copy link
Contributor Author

I'm still seeing in my sandbox in the OP on latest MUI (4.7.1).

image

@b-zurg
Copy link

b-zurg commented Jan 18, 2020

I'm also getting the following error:

TS2322: Type '(event: React.ChangeEvent<{}>, newValue: string) => void' is not assignable to type '((event: ChangeEvent<{}>, value: any) => void) & ((event: FormEvent<HTMLButtonElement>) => void)'.   Type '(event: React.ChangeEvent<{}>, newValue: string) => void' is not assignable to type '(event: FormEvent<HTMLButtonElement>) => void'

I think the key part of this error is the expected type: ((event: ChangeEvent<{}>, value: any) => void) & ((event: FormEvent<HTMLButtonElement>) => void)

The & should be an | it seems as there's no valid function that would match both of those function signatures at the same time that I can see.

@SpotHimesh
Copy link

Having the same error, does anyone have a better fix/hack for it than this?

handleChange = compose((event: React.ChangeEvent<{}>) => {
      // do nothing.
    }, (event: React.FormEvent<HTMLButtonElement>, newValue: any) => {
      setValue(newValue);
});

@JesalR
Copy link

JesalR commented Jan 28, 2020

We are also having this issue
Our (temporary) solve is to use

import Tabs, { TabsProps } from ""@material-ui/core/Tabs"
const HnTabs: typeof Tabs = (props: TabsProps) => <Tabs {...props} />;

But we'd like a better solution, this doesn't feel good

@oliviertassinari
Copy link
Member

@ianschmitz I have seen the exact same bug in #20191. What do you think of this patch #20191 (comment)?

@cristian-eriomenco
Copy link

@oliviertassinari I thought i was going crazy here is a sandbox showing 2 approaches both breaking something and fixing something else.

@ianschmitz
Copy link
Contributor Author

@ianschmitz I have seen the exact same bug in #20191. What do you think of this patch #20191 (comment)?

@oliviertassinari a quick glance seems like that would improve things. To be honest some of those types make me go cross eyed when i read them so it's hard to say without writing out a bunch of test cases 😛

@b-zurg
Copy link

b-zurg commented Jun 21, 2020

I'm just doing this right now. It's messy but I don't really know a better solution

interface TabsPropsFixed extends Omit<TabsProps, "onChange"> {
  onChange:
    | ((event: ChangeEvent<{}>, value: any) => void)
    | ((event: FormEvent<HTMLButtonElement>) => void);
}
export const Tabs: React.FC<TabsPropsFixed> = props => <Tabs {...props}/>

@eps1lon
Copy link
Member

eps1lon commented Jun 23, 2020

Good news: The suggested type was definitely wrong on our part.
Bad news: React.ChangeEvent is not the (technically) correct type (Though React.ChangeEvent<{}> will work incidentally. It's only valid structurally but has implications that don't hold true such as event.target === event.currentTarget.)

We're not creating a custom, react change event in the Tabs component. We're simply passing along whatever event triggered the change in value. I won't go into detail what specific type of events this could be so that we're not needlessly restricting our implementation.

It's a bit unfortunate since it's highly unlikely that you ever relied on specifics of ChangeEvent simply because they don't matter in our case (has something to do with target vs. currentTarget).

I suggest using function handleTabsChange(event: React.SyntheticEvent, tabsValue: unknown) {} since that is going to be the type in v5.

@walltex
Copy link

walltex commented Sep 6, 2020

@eps1lon I still do have the issue. I tried both solutions, nothing works for me.

material-ui@4.11.0

Screen Shot 2020-09-06 at 14 10 43

Screen Shot 2020-09-06 at 14 10 17

UPD: I just realised that this fix was merged into next branch that is 5.* alpha. Is it possible to have this fix cherry-picked to 4.* by any chance?

@eps1lon
Copy link
Member

eps1lon commented Sep 6, 2020

Is it possible to have this fix cherry-picked to 4.* by any chance?

We're only backporting important or security fixes at this stage.

@vinikatyal
Copy link

@eps1lon I still do have the issue. I tried both solutions, nothing works for me.

material-ui@4.11.0

Screen Shot 2020-09-06 at 14 10 43 Screen Shot 2020-09-06 at 14 10 17

UPD: I just realised that this fix was merged into next branch that is 5.* alpha. Is it possible to have this fix cherry-picked to 4.* by any chance?

Were you able to figure this out?

@vinikatyal
Copy link

function handleTabsChange(event: React.SyntheticEvent, tabsValue: unknown) {}

This worked for me

@iiskaandar
Copy link

Still have this issue, and when I am reading "event.target.value" or "event.currentTarget.value" there is "undefined" or "null".

@vinikatyal
Copy link

Still have this issue, and when I am reading "event.target.value" or "event.currentTarget.value" there is "undefined" or "null".

export interface ITabsProps extends Omit<TabsProps, 'onChange' | 'textColor'> {
children?: React.ReactNode;
textColor?: TabsProps['textColor'];
onChange: (event: React.ChangeEvent<{}>, newValue: number) => void;
}

@iiskaandar I used this

@cburgos0511
Copy link

I was able to do this:

  const handleChangeResourceType = (
    event: any,
    newValue: React.SetStateAction<string>
  ): void => {
    setResourceType(newValue);
  };

@tculig

This comment has been minimized.

@nick-buzzy
Copy link

Still an issue, and none of the above proposed solutions worked.

@oliviertassinari
Copy link
Member

@nick-buzzy do you have a reproduction with v5?

@nick-buzzy
Copy link

@nick-buzzy do you have a reproduction with v5?

No I am using the latest stable version, 4.11.

@eglavin
Copy link

eglavin commented Dec 9, 2021

Yeah I'm running in to this issue with the tabs component with these mui versions

{
    "@mui/core": "^5.0.0-alpha.51",
    "@mui/icons-material": "^5.0.4",
    "@mui/lab": "^5.0.0-alpha.51",
    "@mui/material": "^5.0.4",
    "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
    "@mui/styles": "^5.0.1",
    "@mui/system": "^5.0.4",
    "@mui/x-data-grid": "^5.0.0-beta.4",
}

@ghostrydr
Copy link

I know this is a few years old now, but I just came across this issue in our codebase and this is what I was able to come up with without having to create an intermediate type or wrapping the component.

const handleOnChange = (
    event?: FormEvent<HTMLButtonElement> | ChangeEvent<unknown>,
    value?: any, // You can set this to whatever you're actually expecting
  ) => {
    // do things
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: tabs 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.