Home > Software engineering >  Default value from Union type following defaultProps value in React Typescript
Default value from Union type following defaultProps value in React Typescript

Time:09-07

I want to make a Dropdown component with two versions, one that can return multiple selected values and one that returns a single selected value. All of that is determined by a single prop which is called variant. this variant has union type single | multiple. So this is what it looks like:


type MyOption = { label: any; value: any };

type MyDropdownProps =
  | {
      variant?: "single";
      options: [];
      onChange: (values: MyOption) => void;
    }
  | {
      variant?: "multi";
      options: [];
      onChange: (values: MyOption[]) => void;
    };

const MyDropdown = (props: MyDropdownProps) => {
  return <pre>{JSON.stringify(props)}</pre>;
};

const Render = () => {
  return <MyDropdown options={[]} variant="single" onChange={(evt) => {}} />;
};

you see this is work correctly if we specify the variant prop: variant="single"

enter image description here

my question is what if we don't want to specify the variant prop? what if we already specify it from defaultProps?


const MyDropdown = (props: MyDropdownProps) => {
  return <pre>{JSON.stringify(props)}</pre>;
};

MyDropdown.defaultProps = {
  variant: "single"
};

enter image description here

You see the onChange is not returning the correct data, it returns with any type. How do we fix this? I've spent weeks trying to find the answer but still have not found it yet :(

CodePudding user response:

Define your props and component as generic and use the variant type to determine the parameter type for onChange.

Since your variant can only be one of two types, I would opt for a simpler Boolean type determined by the presence of a multiple prop (just like <select>).

interface MyDropDownProps<Multiple extends Boolean> {
  multiple?: Multiple;
  options: MyOption[];
  onChange: (values: Multiple extends true ? MyOption[] : MyOption) => void;
}

You can then define your generic component

const MyDropdown = <Multiple extends Boolean>(
  props: MyDropDownProps<Multiple>
) => {
  return <pre>{JSON.stringify(props)}</pre>;
};

with multiple

without multiple

CodePudding user response:

Consider this example:

import React from 'react'

type MyOption = { label: any; value: any };

type MyDropdownProps =
    {
        options: [];
        onChange: (values: MyOption[] | MyOption) => void;
    };

const MyDropdown = (props: MyDropdownProps) => {
    return <pre>{JSON.stringify(props)}</pre>;
};

const Render = () => {
    return <MyDropdown options={[]} onChange={(evt /* MyOption[] | MyOption */) => { }} />;
};

Playground

If you don't want to specify variant property, then, in fact you don't need a union of props. You need a union of onChange arguments

  • Related