I have this component in React,
import { ChangeEvent } from "react";
type SelectProps<T> = {
options: T[];
value: number;
onChange:
| ((id: number) => void)
| ((e: React.ChangeEvent<HTMLSelectElement>) => void);
labelKey: keyof T;
labelValue: keyof T;
defaultText: string;
};
/**
* Select component to show and to select the options.
* The value that gets selected is the id.
* The labelKey and labelValue is the keyof T, that is the key of the options.
*
* @param options List of objects to display in the Select.
* @param value The selected value.
* @param onChange The onChange method to change the value when selected.
* @param labelKey The text that we want to select when we choose an option.
* @param labelValue The text that we want to show in the dropdown.
* @param defaultText The text to display when the value is 0.
*
* @returns Select component.
*/
const Select = <T extends object>({
options,
value,
onChange,
labelKey,
labelValue,
defaultText,
}: SelectProps<T>) => {
const handleOptionChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedValue = parseInt(event.target.value);
if (typeof onChange === "function") {
if (onChange instanceof Function) {
(onChange as (id: number) => void)(selectedValue);
} else {
(onChange as (e: React.ChangeEvent<HTMLSelectElement>) => void)(event);
}
}
};
return (
<div>
<select
value={value}
onChange={handleOptionChange}
className="p-2 border w-full cursor-pointer bg-white text-gray-dark border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 overflow-y-auto max-h-32"
>
<option value={0}>Select a {defaultText}</option>
{options.map((option) => (
<option
key={option[labelKey]}
value={option[labelKey]}
className="whitespace-nowrap"
>
{option[labelValue]}
</option>
))}
</select>
</div>
);
};
export default Select;
In this component, one of the props that is onChange
, takes two different types of function, and depending on the type of function I want to run the function.
For example when the type is of ((id: number) => void)
I want to run (onChange as (id: number) => void)(selectedValue)
and when the type is of HTMLSelectElement
i want to pass the event.
I can achieve this by passing one of the additional props but is there any way to differentiate these two functions?
I tried a couple of ways like checking type and instance but it didn’t work.
Any suggestions?
2
Answers
This is tricky (or impossible) to do the way you are approaching this because at runtime both functions become
id => void
because when the code is transpiled all type information is lost.typeof
andinstanceof
are operations that are evaluated at runtime.The dirty way to do it is to add an additional property to
SelectProps
You may make it into a type union e.g.
This may better clarify that you need one or the other (not none or both)
You can write a custom type guard function like this: