Home > Net >  Why does extracting a boolean from a union type say that boolean is not equal to true?
Why does extracting a boolean from a union type say that boolean is not equal to true?

Time:11-17

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.

  • Related