• Home
  • Jobs
  • Courses
  • Questions
  • Teachers
  • For business
  • ES/EN

0

56
Views
Why does property spreading work inside Grid but not in FormControl?

Sandbox here: https://codesandbox.io/s/agitated-ardinghelli-fnoj15?file=/src/temp4.tsx:0-1206.

import { FormControl, FormControlProps, Grid, GridProps } from "@mui/material";

interface ICustomControlProps {
  gridProps?: GridProps;
  formControlProps?: FormControlProps;
}

const CustomControl = (props: ICustomControlProps) => {
  return (
    <Grid {...props.gridProps}>
      <FormControl {...props.formControlProps} />
    </Grid>
  );
};

const CustomControlVariant1 = (props: ICustomControlProps) => {
  return (
    <CustomControl
      /**
       * Below gridProps is of type GridProps.
       * It is set to object with some fixed (item, xs, sm, md) properties and spreaded props.gridProps 
       * (which is of type GridProps). 
       * But doing exactly same with formControlProps doesnt work: setting fixed component properties
       * and spreaded props.formControlProps
       */
      gridProps={{ item: true, xs: 12, sm: 12, md: 12, ...props.gridProps }}
      formControlProps={{
        component: "fieldset",  /** ERROR: Type '{ children?: React.ReactNode; classes?: Partial<FormControlClasses> & 
                                  * Partial<ClassNameMap<never>>; color?: "error" | ... 4 more ... | "warning"; ...
                                  * 264 more ...; component: string; }' is not assignable to type 'FormControlProps<"div", {}>'.
                                  * Object literal may only specify known properties, and 'component' does not exist in type 
                                  * 'FormControlProps<"div", {}>'. 
                                  * 
                                  * Object literal may only specify known properties, and 'component' 
                                  * does not exist in type 'FormControlProps<"div", {}>'.ts(2322)
                                  */
        ...props.formControlProps
      }}
    />
  );
};

Update

Note that adding another level of spreading for FormControl works:

<CustomControl
      gridProps={{ item: true, xs: 12, sm: 12, md: 12, ...props.gridProps }}

      // this works!! So, `component` is indeed on `FormControlProps`
      // which can be confirmed here: https://mui.com/api/form-control/#props
      formControlProps={{
        ...{
          component: "fieldset",
          ...props.formControlProps
        }
      }}
    />

Here is the sandbox for this code.

I find both approaches logically correct and in my opinion, both should work, but I don't understand why the first one is giving an error.

5 months ago ·

Santiago Trujillo

1 answers
Answer question

0

TLDR;

In order to make TypeScript happy, you could modify ICustomControlProps as follows:

interface ICustomControlProps {
  gridProps?: GridProps;
  formControlProps?: FormControlProps & { component: React.ElementType };
}

Explanation

Let's have a look in the FormControl's declaration file:

declare const FormControl: OverridableComponent<FormControlTypeMap>;

export type FormControlProps<
  D extends React.ElementType = FormControlTypeMap['defaultComponent'],
  P = {},
> = OverrideProps<FormControlTypeMap<P, D>, D>;

Concisely, FormControlProps will gather together the props of the default component(which is a div - so, some props a specific DOM element can take in) and the props which define the FormControl component(these can be found in FormControlTypeMap, e.g. classes, fullWidth, hiddenLabel etc).

In FormControlTypeMap there surely is no component property defined and hence the error. However, OverridableComponent<FormControlTypeMap> says that receiving a component prop is accepted:

export interface OverridableComponent<M extends OverridableTypeMap> {
  <C extends React.ElementType>(
    props: {
      /**
       * The component used for the root node.
       * Either a string to use a HTML element or a component.
       */
      component: C;
    } & OverrideProps<M, C>,
  ): JSX.Element;
  (props: DefaultComponentProps<M>): JSX.Element;
}

So, you can safely use FormControl like this: <FormControl component={...} />, but wrapping it in another component, like this:

interface ICustomControlProps {
  gridProps?: GridProps;
  formControlProps?: FormControlProps;
}

const CustomControl = (props: ICustomControlProps) => {
  return (
    <Grid {...props.gridProps}>
      <FormControl {...props.formControlProps} />
    </Grid>
  );
};

will require you to specify that a component prop is also allowed on ICustomControlProps.formControlProps:

interface ICustomControlProps {
  gridProps?: GridProps;
  formControlProps?: FormControlProps & { component: React.ElementType };
}

As to why this works:

formControlProps={{
  ...{
    component: "fieldset",
    ...props.formControlProps
  }
}}

the answer seems to be found in this issue, more specifically in this comment, which says that ... creates a subtype, so it's all valid.

5 months ago · Santiago Trujillo Report
Answer question
Find remote jobs
Loading

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post job Plans Our process Sales
Legal
Terms and conditions Privacy policy
© 2022 PeakU Inc. All Rights Reserved.