简体   繁体   中英

Getting onChange value to be of generic type without type assertion in TypeScript

I'v created a generic react component with option elements like this: import * as React from "react";

  export interface Option {
    value: string;
    label: string;
    disabled?: boolean;
  }

  export interface SelectProps<CustomOption extends Option> {
    options: CustomOption[];
    onChange: (event: React.ChangeEvent<HTMLSelectElement>) => void;
    selectedValue: string;
  }

  export const Select: React.FunctionComponent<SelectProps<Option>> = (props): JSX.Element => {
    const options = props.options.map(o => {
      return (
        <option key={o.value} value={o.value} disabled={o.disabled}>
          {o.label}
        </option>
      );
    });

    return (
      <select onChange={props.onChange} value={props.selectedValue}>
        {options}
      </select>
    );
  };

I'm using this like this:

  type OptionValues = "FOO" | "BAR" | "BAZ" | "NO_ANSWER";

  const createSelect = (selectedOption: OptionValues, onChange: (v: OptionValues) => void): JSX.Element => {
    const changeHandler = (event: React.ChangeEvent<HTMLSelectElement>): void => onChange(event.currentTarget.value as OptionValues);
    interface SelectOptions extends Option {
      value: OptionValues;
    }

    const selectProps: SelectProps<SelectOptions> = {
      onChange: changeHandler,
      selectedValue: selectedOption,
      options: [
        {
          value: "FOO",
          label: "foo"
        },
        {
          value: "BAR",
          label: "bar"
        },
        {
          value: "BAZ",
          label: "baz"
        },
        {
          value: "NO_ANSWER",
          label: " -- ",
          disabled: true
        }
      ]
    };
    return <Select {...selectProps} />;
  };

Is there any way to avoid using type assertion in the changeHandler. event.currentTarget.value as OptionValues . Also I see that the whole createSelect suddenly is very verbose with references to OptionValues everywhere, but I guess there is no way around it. Alternative ways to solve the problem is also welcomed. That is making sure value is of predetermined types.

In your case you actually need a type-guard, as typescript is not that smart.

If your Select component deep down, is just what I think it is, it will never work.

See typeguards

type OptionValues = 'FOO' | 'BAR' | 'BAZ' | 'NO_ANSWER'

const isOptionValue = (value: unknown): value is OptionValues =>
  typeof value === 'string' && ['FOO', 'BAR', 'BAZ', 'NO_ANSWER'].includes(value)

  const createSelect = (selectedOption: OptionValues, onChange: (v: OptionValues) => void): JSX.Element => {
    const changeHandler = (event: React.ChangeEvent<HTMLSelectElement>): void => {
        const value = event.currentTarget.value
        if(isOptionValue(value))
         onChange(value);
    }
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM