I have the following component:
// ...
type StringOrNumber = string | number;
type InputProps<T extends StringOrNumber> = {
value: T;
onSubmit: (value: T) => void;
};
export default function Input<T extends StringOrNumber>(props: InputProps<T>) {
const [value, setValue] = useState(props.value.toString());
// Called on enter & blur
const submitValue = () => {
// ts(2345): Argument of type 'string' is not assignable to parameter of type 'T'.
props.onSubmit(value);
};
// ...
return (
<input
{/* ... */}
onChange={(e) => setValue(e.target.value)}
value={value}
/>
);
}
The reason for the error above inside submitValue is quite obvious as value is always of type string here, whilst onSubmit expects a string or a number, depending on the generic T of the component.
The problem is, that I don't know how to properly work around this error. I tried using type guards but that resulted in the same errors:
function isNumber(x: StringOrNumber): x is number {
return typeof x === 'number';
}
// ...
export default function Input<T extends StringOrNumber>(props: InputProps<T>): JSX.Element {
// ...
const submitValue = () => {
if (isNumber(props.value)) {
const numberValue = parseFloat(value) || 0;
// ts(2345): Argument of type 'number' is not assignable to parameter of type 'T'.
props.onSubmit(numberValue);
} else {
// ts(2345): Argument of type 'string' is not assignable to parameter of type 'T'.
props.onSubmit(value);
}
};
// ...
}
I guess the issue is that the type guard is only checking the type of props.value here whilst it should somehow check what param type onSubmit actually expects.
How is this done correctly?