start-ui-web icon indicating copy to clipboard operation
start-ui-web copied to clipboard

TypeScript Error TS2740

Open winner106 opened this issue 3 years ago โ€ข 4 comments

When I try adding leftIcon prop to component Select, I need to custom ValueContainer of React-Select.

But I ran into a typescript error ts(2740),

image

err message as follows:

image

(property) ValueContainer: <Option_19, IsMulti_19 extends boolean, Group_19 extends GroupBase<Option_19>>(props: ValueContainerProps<Option_19, IsMulti_19, Group_19>) => jsx.JSX.Element
Type '{ children: any[]; }' is missing the following properties from type 'ValueContainerProps<unknown, boolean, GroupBase<unknown>>': isDisabled, clearValue, cx, getStyles, and 9 more.ts(2740)

How can I fix this error? Any help would be appreciated! Thanks

winner106 avatar Feb 12 '22 03:02 winner106

I think you will need to type the props of the ValueContainer (or use any ๐Ÿ˜…)

Also you should NEVER define a component inside another component in React โš ๏ธ

If you want more help on this, please share plain text code (that I can paste into my project) ๐Ÿ˜‰

ivan-dalmet avatar Feb 22 '22 11:02 ivan-dalmet

import React, { ReactNode, useRef } from 'react';

import { Box, BoxProps, useStyleConfig, useTheme, useToken } from '@chakra-ui/react';
import type { CSSObject } from '@emotion/react';

import ReactSelect, { GroupBase, Props, components } from 'react-select';
import AsyncReactSelect from 'react-select/async';
import AsyncCreatableReactSelect from 'react-select/async-creatable';
import CreatableReactSelect from 'react-select/creatable';

import { useDarkMode } from '@/hooks/useDarkMode';

const BoxAny: any = Box;

// Tricks for generic forwardRef. Do not move this declaration elsewhere as we
// do not want to apply it everywhere. The duplication is not a problem itself
// as this code won't be in the final bundle.
// https://fettblog.eu/typescript-react-generic-forward-refs/#option-3%3A-augment-forwardref
declare module 'react' {
  function forwardRef<T, P = {}>(
    render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
  ): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}

export type SelectProps<
  Option,
  IsMulti extends boolean = false,
  Group extends GroupBase<Option> = GroupBase<Option>
> = {
  isAsync?: boolean;
  isCreatable?: boolean;
  isError?: boolean;
  size?: string;
  formatCreateLabel?: (inputValue: string) => ReactNode;
  loadOptions?: (input: unknown) => any;
  defaultOptions?: unknown | boolean;
  debounceDelay?: number;
  leftIcon?: any;
} & Props<Option, IsMulti, Group> &
  Omit<BoxProps, 'defaultValue'>;

const SelectInner = <Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>(
  props: SelectProps<Option, IsMulti, Group>,
  ref: React.ForwardedRef<HTMLElement>
) => {
  const {
    isAsync,
    isCreatable,
    isError,
    size = 'md',
    loadingMessage,
    formatCreateLabel,
    placeholder,
    loadOptions = () => new Promise<void>((resolve) => resolve()),
    defaultOptions = true,
    debounceDelay = 500,
    styles = {},
    leftIcon,
    ...otherProps
  } = props;

  const theme = useTheme();
  const { colorModeValue } = useDarkMode();
  const stylesFromTheme: any = useStyleConfig('Select', {
    size,
  });
  const [fieldFontSize] = useToken('fontSizes', [stylesFromTheme.field.fontSize]);
  const [fieldTextColor, fieldBg, fieldBorderColor, fieldFocusBorderColor, fieldErrorBorderColor] = useToken('colors', [
    stylesFromTheme.field.color,
    stylesFromTheme.field.bg,
    stylesFromTheme.field.borderColor,
    stylesFromTheme.field._focus.borderColor,
    stylesFromTheme.field._invalid.borderColor,
  ]);
  const [fieldBorderRadius] = useToken('radii', [stylesFromTheme.field.borderRadius]);
  const [fieldHeight] = useToken('sizes', [stylesFromTheme.field.h]);

  const Element = (() => {
    if (isAsync && isCreatable) return AsyncCreatableReactSelect;
    if (isAsync) return AsyncReactSelect;
    if (isCreatable) return CreatableReactSelect;
    return ReactSelect;
  })();

  let debounceTimeout = useRef<any>();

  const debounce = (func: () => unknown, delay: number) => {
    clearTimeout(debounceTimeout.current);
    return new Promise((resolve) => {
      debounceTimeout.current = setTimeout(async () => {
        resolve(func());
      }, delay);
    });
  };

  const asyncProps = isAsync
    ? {
        defaultOptions,
        cacheOptions: true,
        loadOptions: (input: unknown) => debounce(() => loadOptions(input), debounceDelay),
      }
    : {};

  type state = {
    isDisabled: boolean;
    isFocused: boolean;
    isSelected: boolean;
  };

  const getComponentStyles = (componentName: string, callback: (state: state) => CSSObject) => ({
    [componentName]: (provided: CSSObject, state: state) => {
      const componentsStyles = callback(state);
      const combinedStyles = {
        ...provided,
        ...componentsStyles,
      };
      return styles?.[componentName]?.(combinedStyles, state) ?? combinedStyles;
    },
  });

  const getConditionalStyles = (condition: boolean, conditionalStyles: CSSObject): CSSObject =>
    condition ? conditionalStyles : {};

  const selectStyle = {
    ...styles,
    ...getComponentStyles('input', () => ({
      color: fieldTextColor,
    })),
    ...getComponentStyles('singleValue', () => ({
      color: fieldTextColor,
    })),
    ...getComponentStyles('valueContainer', () => ({
      minHeight: `calc(${fieldHeight} - 2px)`,
      padding: '0',
      paddingLeft: !!leftIcon ? '33px' : '8px', //้ข„็•™iconไฝ็ฝฎ
      paddingRight: '8px',
    })),
    ...getComponentStyles('indicatorsContainer', () => ({
      height: `calc(${fieldHeight} - 2px)`,
    })),
    ...getComponentStyles('multiValue', () => ({
      backgroundColor: colorModeValue(theme.colors.brand['100'], theme.colors.brand['300']),
    })),
    ...getComponentStyles('multiValueLabel', () => ({
      fontWeight: 'bold',
      color: colorModeValue(theme.colors.brand['800'], theme.colors.brand['900']),
    })),
    ...getComponentStyles('multiValueRemove', () => ({
      color: colorModeValue(theme.colors.brand['800'], theme.colors.brand['900']),
      opacity: 0.5,
      '&:hover': {
        background: 'transparent',
        color: colorModeValue(theme.colors.brand['800'], theme.colors.brand['900']),
        opacity: 1,
      },
    })),
    ...getComponentStyles('dropdownIndicator', () => ({
      paddingLeft: '0',
      paddingRight: '0.2rem',
    })),
    ...getComponentStyles('indicatorSeparator', () => ({
      display: 'none',
    })),
    ...getComponentStyles('control', ({ isFocused, isDisabled }) => ({
      color: fieldTextColor,
      fontSize: fieldFontSize,
      height: 'fit-content',
      minHeight: fieldHeight,
      borderRadius: fieldBorderRadius,
      borderColor: fieldBorderColor,
      backgroundColor: fieldBg,
      ...getConditionalStyles(isFocused && fieldFocusBorderColor, {
        borderColor: fieldFocusBorderColor,
        boxShadow: `0 0 0 1px ${fieldFocusBorderColor}`,
        '&:hover': {
          borderColor: fieldFocusBorderColor,
        },
      }),
      ...getConditionalStyles(isError, {
        borderColor: fieldErrorBorderColor,
        boxShadow: `0 0 0 1px ${fieldErrorBorderColor}`,
        '&:hover': {
          borderColor: fieldErrorBorderColor,
        },
      }),
      ...getConditionalStyles(isDisabled, {
        opacity: 0.6,
      }),
    })),
    ...getComponentStyles('option', ({ isFocused, isDisabled, isSelected }) => ({
      fontSize: fieldFontSize,
      ':active': {
        backgroundColor: colorModeValue(theme.colors.gray['100'], theme.colors.blackAlpha['500']),
      },
      ...getConditionalStyles(isFocused, {
        backgroundColor: colorModeValue(theme.colors.gray['100'], theme.colors.blackAlpha['400']),
        color: colorModeValue(theme.colors.gray['600'], theme.colors.gray['100']),
      }),
      ...getConditionalStyles(isSelected, {
        backgroundColor: colorModeValue(theme.colors.gray['50'], theme.colors.blackAlpha['500']),
        color: colorModeValue(theme.colors.gray['700'], 'white'),
        borderLeft: `2px solid ${theme.colors.brand['500']}`,
      }),
      ...getConditionalStyles(isFocused && isSelected, {
        backgroundColor: colorModeValue(theme.colors.gray['100'], theme.colors.blackAlpha['400']),
        color: colorModeValue(theme.colors.gray['600'], theme.colors.gray['100']),
      }),
      ...getConditionalStyles(isDisabled, {
        opacity: 0.4,
      }),
    })),
    ...getComponentStyles('menu', () => ({
      zIndex: 10,
      backgroundColor: colorModeValue('white', theme.colors.gray['700']),
    })),
    ...getComponentStyles('menuPortal', () => ({
      zIndex: theme.zIndices.select,
    })),
  };

  // add leftIcon
  const ValueContainer = ({ children, ...props }) =>
    components.ValueContainer && (
      <components.ValueContainer {...props}>
        {!!children && !!leftIcon && <span style={{position:'absolute' ,left:10}}>{leftIcon}</span>}
        {children}
      </components.ValueContainer>
    );

  return (
    <BoxAny
      as={Element}
      styles={selectStyle}
      menuPortalTarget={document.body}
      {...(loadingMessage ? { loadingMessage: () => loadingMessage } : {})}
      {...(formatCreateLabel ? { formatCreateLabel } : {})}
      placeholder={placeholder ? String(placeholder) : 'Select...'}
      components={{ ValueContainer }}
      menuPlacement="auto"
      ref={ref}
      {...asyncProps}
      {...otherProps}
    />
  );
};

export const Select = React.forwardRef(SelectInner);

winner106 avatar Feb 23 '22 07:02 winner106

I think you will need to type the props of the ValueContainer (or use any ๐Ÿ˜…)

Also you should NEVER define a component inside another component in React โš ๏ธ

If you want more help on this, please share plain text code (that I can paste into my project) ๐Ÿ˜‰

Thank you very much.

winner106 avatar Feb 23 '22 07:02 winner106

When I try adding leftIcon prop to component Select, I need to costom ValueContainer of React-Select.

But I ran into a typescript error ts(2740),

image

err message as follows:

image

(property) ValueContainer: <Option_19, IsMulti_19 extends boolean, Group_19 extends GroupBase<Option_19>>(props: ValueContainerProps<Option_19, IsMulti_19, Group_19>) => jsx.JSX.Element
Type '{ children: any[]; }' is missing the following properties from type 'ValueContainerProps<unknown, boolean, GroupBase<unknown>>': isDisabled, clearValue, cx, getStyles, and 9 more.ts(2740)

How can I fix this error? Any help would be appreciated! Thanks

winner106 avatar Feb 23 '22 07:02 winner106