Cómo convertir un valor dentro de un componente funcional basado en un tipo genérico.
Tengo el siguiente componente:
// ...
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());
// Llamado al presionar enter y al perder el foco
const submitValue = () => {
// 🔥 ts(2345): El argumento de tipo 'string' no se puede asignar al parámetro de tipo 'T'.
props.onSubmit(value);
};
// ...
return (
<input {/*="" ...="" */}="" onchange="{(e)" ==""> setValue(e.target.value)}
value={value}
/>
);
}
La razón del error anterior dentro de submitValue
es bastante obvia ya que value
siempre es de tipo string
aquí, mientras que onSubmit
espera un string
o un number
, dependiendo del genérico T
del componente.
El problema es que no sé cómo solucionar correctamente este error. Intenté usar guardias de tipo, pero eso resultó en los mismos errores:
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): El argumento de tipo 'number' no se puede asignar al parámetro de tipo 'T'.
props.onSubmit(numberValue);
} else {
// 🔥 ts(2345): El argumento de tipo 'string' no se puede asignar al parámetro de tipo 'T'.
props.onSubmit(value);
}
};
// ...
}
Supongo que el problema es que la guarda de tipo solo está verificando el tipo de props.value
aquí, mientras que de alguna manera debería verificar qué tipo de parámetro espera realmente onSubmit
.
¿Cómo se hace esto correctamente?
davy.ai
Una posible solución es utilizar la sobrecarga de funciones y crear dos interfaces diferentes
InputProps
, una para valores de tipostring
y otra para valores de tiponumber
. De esta manera, al crear un componenteInput
, podemos especificar explícitamente el tipo deprops
y evitar errores de tipo en tiempo de compilación. Aquí tienes un ejemplo de implementación:En esta implementación, definimos dos interfaces separadas para
StringInputProps
yNumberInputProps
, las cuales especifican la firma correcta de la funciónonSubmit
para cada tipo de valor. Luego combinamos estas interfaces en un tipo de uniónInputProps
que puede aceptar tantoStringInputProps
comoNumberInputProps
.Dentro del componente, usamos un guardia de tipo para verificar el tipo de
props.value
y llamar a la funciónonSubmit
correspondiente. Esto nos permite evitar errores de tipo en tiempo de compilación y asegurarnos de que se pasen los tipos de valor correctos a la funciónonSubmit
.