import React, { FC } from 'react';
// Definición de tipos
interface Option {
label: string;
value: string;
}
interface SelectBoxBaseProps {
options: Option[];
// ...
}
// Extiende SelectBoxBaseProps para selección múltiple
interface SelectBoxPropsMulti extends SelectBoxBaseProps {
value: string[];
onChange: (value: Option[]) => void;
isMulti: true;
}
// Extiende SelectBoxBaseProps para selección simple
interface SelectBoxPropsSingle extends SelectBoxBaseProps {
value: string;
onChange: (value: Option) => void;
isMulti?: false;
}
// Combinación de ambos tipos de props utilizando la unión de tipos
type SelectBoxProps = SelectBoxPropsMulti | SelectBoxPropsSingle;
const SelectBox: FC<SelectBoxProps> = ({ options, value, onChange, isMulti }) => {
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
if (isMulti) {
const selectedOptions = Array.from(event.target.selectedOptions, option => ({
value: option.value,
label: option.label,
}));
onChange(selectedOptions);
} else {
const selectedOption = {
value: event.target.value,
label: event.target.options[event.target.selectedIndex].label,
};
onChange(selectedOption);
}
};
return (
<select multiple={isMulti} value={isMulti ? value : [value]} onChange={handleChange}>
{options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
};
import React from 'react';
interface Option {
label: string;
value: string;
}
type OnChangeValue<Option, IsMulti extends boolean> = IsMulti extends true
? Option[]
: Option;
type SelectBoxProps<IsMulti extends boolean> = {
options: Option[];
value: IsMulti extends true ? string[] : string;
onChange: (value: OnChangeValue<Option, IsMulti>) => void;
isMulti: IsMulti;
};
const SelectBox = <IsMulti extends boolean>({ options, value, onChange, isMulti }: SelectBoxProps<IsMulti>) => {
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
let selectedOptions;
if (isMulti) {
selectedOptions = Array.from(event.target.selectedOptions, ({ value, label }) => ({ value, label }));
} else {
const { value, label } = event.target.options[event.target.selectedIndex];
selectedOptions = { value, label };
}
onChange(selectedOptions as OnChangeValue<Option, IsMulti>);
};
return (
<select multiple={isMulti} value={Array.isArray(value) ? value : [value]} onChange={handleChange}>
{options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
};
export default SelectBox;
usage this component in differents usecases:
import SelectBox from './components/SelectBox'
function App() {
const options = [
{ value: 'xx', label: 'XX' },
{ value: 'yy', label: 'YY' },
{ value: 'zz', label: 'ZZ' },
];
return (
<>
<p>SelectBox isMulti=false</p>
<SelectBox
isMulti={false}
options={options}
value={''}
onChange={(value) =>{
console.log({ value })
// ERROR TS
// value.map((v) => console.log({ v }))
}}
/>
<hr />
<p>SelectBox isMulti=true</p>
<SelectBox
isMulti={true}
options={options}
value={['']}
onChange={(value) =>{
console.log({ value })
// IT WORK
value.map((v) => console.log({ v }))
}}
/>
</>
)
}
export default App