最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - MUI v5 Autocomplete. How to dynamically define argument type in onChange callback based on disableClearable? - Stac

programmeradmin3浏览0评论

I am trying to define reusable Autocomplete component with MUI Autocomplete as a foundation.

I want to define my own onChange callback (for a property) that accepts:

  • string if DisableClearable is true
  • string | null if DisableClearable is false

Each predefined option is object with 2 fields: { label: string, value: string }

So I assume that onChange value maybe be Value | string | null

  • Value - object is label and value
  • string - if freeSolo
  • null - if DisableClearable false

Here is my onChange:

      onChange={(_, value: Value | string | null) => {
        onChange((typeof value === 'string' ? value : value?.value) || null);
      }}

That worked perfectly while DisableClearable was always false But now I define DisableClearable generic

export type AutocompleteProps<
  Value extends LabelValuePair,
  FreeSolo extends boolean,
  DisableClearable extends boolean,
> = Omit<
  MuiAutocompleteProps<Value, false, DisableClearable, FreeSolo>,
  | 'renderInput'
  | 'onChange'
  | 'multiple'
  | 'autoSelect'
  | 'selectOnFocus'
  | 'clearOnBlur'
  | 'handleHomeEndKeys'
> & {
  error?: boolean;
  onChange: (value: DisableClearable extends true ? string : string | null) => void;
};

And now for

onChange={(_, value: Value | string | null) => {
        onChange((typeof value === 'string' ? value : value?.value) || null);
      }}

I receive TS error:

TS2345: Argument of type string | null is not assignable to parameter of type
DisableClearable extends true ? string : string | null
Type null is not assignable to type
DisableClearable extends true ? string : string | null

So I wonder how to resolve this issue and allow ts to infer type of onChange dynamically

I expect that I can pass to the component: (value: string) => void if DisableClearable is true (value: string | null) => void if DisableClearable is false

Here is full component:

export type AutocompleteProps<
  Value extends LabelValuePair,
  FreeSolo extends boolean,
  DisableClearable extends boolean,
> = Omit<
  MuiAutocompleteProps<Value, false, DisableClearable, FreeSolo>,
  | 'renderInput'
  | 'onChange'
  | 'multiple'
  | 'autoSelect'
  | 'selectOnFocus'
  | 'clearOnBlur'
  | 'handleHomeEndKeys'
> & {
  error?: boolean;
  onChange: (
    value: DisableClearable extends true ? string : string | null,
  ) => void;
};

export const Autocomplete = <
  Value extends LabelValuePair,
  FreeSolo extends boolean,
  DisableClearable extends boolean,
>({
  error,
  onChange,
  freeSolo,
  value,
  ...props
}: AutocompleteProps<Value, FreeSolo, DisableClearable>) => {
  const freeSoloParams = freeSolo
    ? {
        selectOnFocus: true,
        clearOnBlur: true,
        handleHomeEndKeys: true,
      }
    : {};

  // we are trying to find selected option in options otherwise using value (for example when free solo)
  const selectedValue = useMemo(
    () => props.options.find((option) => option.value === value) || value,
    [props.options, value],
  );

  return (
    <MuiAutocomplete
      {...props}
      {...freeSoloParams}
      value={selectedValue}
      filterOptions={(options, params) => {
        const filter = createFilterOptions<Value>();
        const filtered = filter(options, params);

        if (!freeSolo) {
          return filtered;
        }

        const { inputValue } = params;
        const isExisting = options.some(
          (option) => inputValue === option.label,
        );
        if (inputValue && !isExisting) {
          filtered.push({
            value: inputValue,
            label: `Add "${inputValue}"`,
          } as Value);
        }

        return filtered;
      }}
      freeSolo={freeSolo}
      onChange={(_, value: Value | string | null) => {
        onChange((typeof value === 'string' ? value : value?.value) || null);
      }}
      renderInput={(params) => <Input {...params} error={error} />}
    />
  );
};

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论