drecunion
drecunion

Reputation: 121

Unsightly problem with "argument of type string ist not assignable to param of custom type"

i got a working typescript code, see the snippet. Problem with TS is, that it states the code as falsy with the message:

Argument of type 'string' is not assignable to parameter of type 'SetStateAction<"vertical" | "horizontal" | "vertical-reverse" | "horizontal-reverse">'.

How can i write this code cleaner? I thought about using types or enums but then i get to many unecessary casts which then decrease readability and it turns into spaghetti code since i write way to much code for that.

Here the Code:

const directionItems = [
    { key: 0, label: "vertical" },
    { key: 1, label: "horizontal" },
    { key: 2, label: "vertical-reverse" },
    { key: 3, label: "horizontal-reverse" }
  ];

  const [curDirection, setCurDirection] = React.useState<
    "vertical" | "horizontal" | "vertical-reverse" | "horizontal-reverse"
  >("vertical");

<DropDown
   items={directionItems}
   defaultSelectedIndex={3}
   onSelectionChange={value =>
   setCurDirection(directionItems[value].label) // <-- this is where the compiler gets angry even if it just works
 }></DropDown>

Also here the approach using enum, but in my opinion way to much code or is this legit? (no errors from compiler with this approach):

enum position {
    "top",
    "bottom",
    "left",
    "right",
    "in-between"
  }

  const buttonPositionItems = [
    { key: 0, label: position[0] },
    { key: 1, label: position[1] },
    { key: 2, label: position[2] },
    { key: 3, label: position[3] },
    { key: 4, label: position[4] }
  ];

  const [curButtonPosition, setButtonPosition] = React.useState<position>(
    position.right
  );

 <DropDown
    items={buttonPositionItems}
    defaultSelectedIndex={3}
    onSelectionChange={value =>
      setButtonPosition(
      (buttonPositionItems[value].label as unknown) as position)
    }
></DropDown>

My Solution now (i didnt use the interface though..)

export type DirectionType = | "vertical" | "horizontal" | "vertical-reverse" | "horizontal-reverse";

const directionItems = [
    { key: 0, label: "vertical" },
    { key: 1, label: "horizontal" },
    { key: 2, label: "vertical-reverse" },
    { key: 3, label: "horizontal-reverse" }
  ];

  const [curDirection, setCurDirection] = React.useState<DirectionType>(
    "vertical"
  );

<DropDown
  onSelectionChange={value =>
     setCurDirection(directionItems[value].label as DirectionType )
     }>/<DropDown>

Upvotes: 1

Views: 744

Answers (1)

wentjun
wentjun

Reputation: 42536

You might want to work with interfaces to provide explicit typings, such that TypeScript will recognise that label is of type "vertical" | "horizontal" | "vertical-reverse" | "horizontal-reverse". For instance, you could make use of type aliases.

export type LabelType = ("vertical" | "horizontal" | "vertical-reverse" | "horizontal-reverse");

export interface DirectionItem {
  key: number;
  label: LabelType;
}

Now, we can use the interfaces with your React Functional Component.

const directionItems: DirectionItem[] = [
    { key: 0, label: "vertical" },
    { key: 1, label: "horizontal" },
    { key: 2, label: "vertical-reverse" },
    { key: 3, label: "horizontal-reverse" }
  ];

const [curDirection, setCurDirection] = React.useState<LabelType>("vertical");

Upvotes: 1

Related Questions