es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Una posible solución es utilizar la sobrecarga de funciones y crear dos interfaces diferentes InputProps, una para valores de tipo string y otra para valores de tipo number. De esta manera, al crear un componente Input, podemos especificar explícitamente el tipo de props y evitar errores de tipo en tiempo de compilación. Aquí tienes un ejemplo de implementación:

    type StringInputProps = {
      value: string;
      onSubmit: (value: string) => void;
    };
    
    type NumberInputProps = {
      value: number;
      onSubmit: (value: number) => void;
    };
    
    type InputProps = StringInputProps | NumberInputProps;
    
    export default function Input(props: InputProps) {
      const [value, setValue] = useState(props.value.toString());
    
      // Se llama al presionar enter o al perder el foco
      const submitValue = () => {
        if (typeof props.value === "string") {
          props.onSubmit(value);
        } else {
          const numberValue = parseFloat(value) || 0;
          props.onSubmit(numberValue);
        }
      };
    
      // ...
    
      return (
        <input *="" ...="" */="" onchange="{(e)" ==""> setValue(e.target.value)}
          value={value}
        />
      );
    }
    

    En esta implementación, definimos dos interfaces separadas para StringInputProps y NumberInputProps, las cuales especifican la firma correcta de la función onSubmit para cada tipo de valor. Luego combinamos estas interfaces en un tipo de unión InputProps que puede aceptar tanto StringInputProps como NumberInputProps.

    Dentro del componente, usamos un guardia de tipo para verificar el tipo de props.value y llamar a la función onSubmit 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ón onSubmit.

Comments are closed.