Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Custom Hooks with TypeScript – return value

I’m trying to experiment custom Hooks with TS (quite new on both). This useOption should take etiher a number or a boolean, and return the value with the same type, and the changer function. This because an option should be <input> with ‘range’ type, and one <input> as checkbox (but this could be extended with other input types). So I have an <Options /> component like this:

const Options = () => {
  const [pieces, setPieces] = useOption(10);
  const [speed, setSpeed] = useOption(5);
  const [sound, setSound] = useOption(false);

  return (
    <div className='Options'>
      <input
        type='range'
        value={pieces}
        onChange={setPieces}
        min='5'
        max='25' />
      <p>{pieces}</p>
      <input
        type='range'
        value={speed}
        onChange={setSpeed}
        min='1'
        max='10' />
      <p>{speed}</p>
      <input
        type='checkbox'
        value={sound}
        onChange={setSound} />
      <p>{sound ? 'Sound on' : 'Sound off'}</p>
      <button
        className='start-btn'
        onClick={handleClick}>Start game</button>
    </div>
  )
};

And a custom hook useOption.

const useOption = (initialValue: number | boolean) => {
  const [value, setValue] = useState(initialValue);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (typeof(e.currentTarget.value) === 'boolean') {
      const newValue = !value;
      setValue(newValue);     
    } 
    else {
      const newValue = Number(e.currentTarget.value);
      setValue(newValue)
    }
  };

  return [value, handleChange] as const;
};

I thought, that with the typeof type guard and the use of const (like written in this page), TS recognized the returned type, but I still have errors about it, showing me the error over the value property.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Type 'number | boolean' is not assignable to type 'string | number | readonly string[] | undefined'.
  Type 'false' is not assignable to type 'string | number | readonly string[] | undefined'.ts(2322)
index.d.ts(2256, 9): The expected type comes from property 'value' which is declared here on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'

Any suggestion about to solve it?

>Solution :

You can make the hook generic

const useOption = <T extends number | boolean>(initialValue: T) => {
  const [value, setValue] = React.useState(initialValue);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (typeof e.currentTarget.value === 'boolean') {
      const newValue = !value;
      setValue(newValue as T);
    } else {
      const newValue = Number(e.currentTarget.value);
      setValue(newValue as T);
    }
  };

  return [value, handleChange] as const;
};

And pass type in the component

export default function App() {
  const [pieces, setPieces] = useOption<number>(10);
  const [speed, setSpeed] = useOption<number>(5);
  const [sound, setSound] = useOption<boolean>(false);

  return (
    <div className="Options">
      <input
        type="range"
        value={pieces}
        onChange={setPieces}
        min="5"
        max="25"
      />
      <p>{pieces}</p>
      <input type="range" value={speed} onChange={setSpeed} min="1" max="10" />
      <p>{speed}</p>
      <input type="checkbox" checked={sound} onChange={setSound} /> // use checked in here
      <p>{sound ? 'Sound on' : 'Sound off'}</p>
    </div>
  );
}

Demo

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading