Search code examples
reactjstypescriptmaterial-uimui

MUIv4 Upgrade w/ TypeScript - Cannot spread props to TextField Properly


Current Material-UI Version: 4.1.0

I am having issues spreading props to an abstracted <TextField /> component I've created.

Here is the code:

PasswordInput.tsx

import * as React from 'react'
import TextField, { Props as TextFieldProps } from 'src/TextField'

type Props = TextFieldProps & {
  hideHelperText?: boolean;
};

class PasswordInput extends React.Component<Props> {
  onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    /** do stuff */
  };

  render() {
    const {
      hideHelperText,
      classes,
      ...rest
    } = this.props;

    return (
      <React.Fragment>
        <TextField
          /** errors out here */
          /*
            Type 'HideShowText | null' is not assignable to type 'HTMLDivElement | null'.
            Type 'HideShowText' is missing the following properties from type 'HTMLDivElement':
            align, addEventListener, removeEventListener, accessKey, and 238 more
          */
          {...rest}
          onChange={this.onChange}
        />
        {!hideHelperText && 'hello world'}
      </React.Fragment>
    );
  }
}

export default PasswordInput;

TextField.tsx

import TextField, {
  StandardTextFieldProps as TextFieldProps
} from '@material-ui/core/TextField';

type ClassNames =
  | 'root'

const styles = (theme: Theme) =>
  createStyles({
    root: {},
  });

interface BaseProps {
  tooltipText?: string;
  dataAttrs?: Record<string, any>
}

export type Props = BaseProps & TextFieldProps

type CombinedProps = Props &
  WithStyles<ClassNames>;

class MyTextField extends React.Component<CombinedProps> {
  render() {
    const {
      children,
      tooltipText,
      dataAttrs,
      /** everything else that goes on the root */
      ...textFieldProps
    } = this.props;

    return (
      <div
      >
        <TextField
          {...textFieldProps}
          {...dataAttrs}
        >
          {this.props.children}
        </TextField>
        {tooltipText && <HelpIcon text={tooltipText} />}
      </div>
    );
  }
}

const styled = withStyles(styles);

export default compose<CombinedProps, Props>(
  styled
)(MyTextField);

It's bombing with the following error:

TS2322: Type '{ tooltipText: string | undefined; value: string | undefined; onChange: (e: ChangeEvent<HTMLInputElement>) => void; fullWidth: true; required: boolean | undefined; errorText?: string | undefined; ... 282 more ...; innerRef?: ((instance: any) => void) | ... 2 more ... | undefined; }' is not assignable to type 'IntrinsicClassAttributes<HideShowText>'.
  Types of property 'ref' are incompatible.
    Type '((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined' is not assignable to type 'string | ((instance: HideShowText | null) => void) | RefObject<HideShowText> | null | undefined'.
      Type '(instance: HTMLDivElement | null) => void' is not assignable to type 'string | ((instance: HideShowText | null) => void) | RefObject<HideShowText> | null | undefined'.
        Type '(instance: HTMLDivElement | null) => void' is not assignable to type '(instance: HideShowText | null) => void'.
          Types of parameters 'instance' and 'instance' are incompatible.
            Type 'PasswordInput | null' is not assignable to type 'HTMLDivElement | null'.
              Type 'PasswordInput' is missing the following properties from type 'HTMLDivElement': align, addEventListener, removeEventListener, accessKey, and 238 more.

It appears that the ...rest in the PasswordInput should be equal to the StandardTextFieldProps but for some reason I'm getting errors for my component not being an HTMLDivElement?

Let me know if I can provide more detail. As far as I can tell, this is a bug.


Solution

  • I ran into this exact error yesterday. I'm doing the same type union in other places, such as with SelectProps, without an issue, but TextFieldProps is problematic on multiple properties and StandardTextFieldProps on just ref. Therefore, as a temporary measure you could declare:

    export type MyTextFieldProps = Omit< StandardTextFieldProps, 'ref' > & { ... }