start-ui-web
start-ui-web copied to clipboard
TypeScript Error TS2740
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),
err message as follows:
(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
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) ๐
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);
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.
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),
err message as follows:
(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