This is a general understanding problem that I am having. While it is derived from material-ui, I believe it is a general typescript issue. I was not able to re-create the problem with my own types so I will just use material-ui to explain the issue. Working with the codesandbox will make it way easier to explore the types: codesandbox
Here is the code:
import * as React from "react";
import InputUnstyled, { InputUnstyledProps } from "@mui/base/InputUnstyled";
const CustomInput = ({
multiline,
...props
}: InputUnstyledProps): JSX.Element => {
// V Error here
return <InputUnstyled multiline={multiline} {...props} />;
};
export default function UnstyledInputBasic() {
return <CustomInput multiline={false} />;
}
Ignore contrived extraction of multiline. The error states that Types of property 'multiline' are incompatible. Type 'boolean' is not assignable to type 'true'
.
This makes sense when you dive a bit deeper. InputUnstyledProps
is (more-or-less) defined as:
(SingleLineInputUnstyledProps | MultiLineInputUnstyledProps) & UseInputParameters & { ...input stuff }
The key here being SingleLineInput
vs MultiLineInput
which are mutually exclusive. SingleLineInput
hardcodes multiline
to false
. MultiLineInput
sees multiline
hardcoded as true
. This also makes sense from a HTML standpoint. Can't have a single line input with multiline.
As a result, I can't pass booleans into multiline
. I tried hardcoding the other values that go along with a true
or false
multiline
value but it still didn't work:
<InputUnstyled
multiline={multiline}
maxRows={multiline ? 12 : undefined}
minRows={multiline ? 4 : undefined}
rows={multiline ? 2 : undefined}
type={multiline ? undefined : "text"}
{...props}
/>
Where is the gap in my understanding?
CodePudding user response:
Typescript will simply look at the types of the expression you pass in.
multiline
is boolean
maxRows
is number | undefined
etc..
So in the eyes of the typesystem, you could potentially get false and number here.
To make it understand that the different props actually depend on each other, you need to use type narrowing properly.
Something like
const CustomInput = ({
multiline,
type,
maxRows,
minRows,
rows,
...props
}: InputUnstyledProps): JSX.Element => {
return multiline ? (
<InputUnstyled
multiline
maxRows={maxRows ?? 12}
minRows={minRows ?? 4}
rows={rows ?? 2}
{...props}
/>
) : (
<InputUnstyled type={type} {...props} />
);
};
will do the trick. Here it can clearly see that if multiline is true, then and only then will the different row props be used.
Type will only be used if multiline is false and so on.