0

I have created this codesandbox replicating my issue

  • 1) I first created the <Input> component, (for styling and track if the input has content or not.
  • 2) Everything works, but as need to add more forms to the project, i thought, damn maybe i could also created an useInput hook to just manage the value update instead of having to add onChange: {e => {setSomething(e.target.value)}} all the time.

So i created these useInput, but i got this annoying red linter errors. Its probably some basic type issue, but i can figure it out how to get rid of this issue. without any type solutions ? Thanks in advance

Error screenshot and chunk of code below, but better test in the sandbox

enter image description here

# useInput.tsx

import { useState, ChangeEvent } from "react";

export type onChangeType = (event: ChangeEvent<HTMLInputElement>) => void;
const useInput = (initialValue = "") => {
  const [value, setValue] = useState(initialValue);

  const reset = () => setValue("");

  const onChange: onChangeType = e => {
    setValue(e.target.value);
  };

  return [value, onChange, reset];
};

export default useInput;

# Input.tsx

import React, { useState, ChangeEvent } from "react";
import styled, { css } from "styled-components";

import onChangeType from "./hooks/useInput";

interface iLabelProps {
  hasContent: boolean;
}

const hasContentCSS = () => css`
  border: 5px solid royalblue;
`;

const Label = styled.label<iLabelProps>```

interface iInput {
  readonly type?: string;
  readonly name: string;
  readonly label: string;
  value?: string | number | string[] | null;
  defaultValue?: string | number | string[] | null;
  readonly onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
}

export const Input = ({
  name = "email",
  label,
  value = null,
  defaultValue = null,
  onChange = null
}: iInput) => {
  const [hasContent, setHasContent] = useState(!!defaultValue);

  const onBlur = value => {
    setHasContent(value.length > 0);
  };

  return (
    <Label hasContent={hasContent}>
      <input
        type="text"
        name={name}
        {...defaultValue && { defaultValue: defaultValue }}
        {...!defaultValue && { value: value ? value : "" }}
        {...onChange && { onChange: onChange }}
        onBlur={e => onBlur(e.target.value)}
      />
      <span>{label}</span>
    </Label>
  );
};

1 Answer 1

1

The problem came from the incorrectly inferred type of the returned value from the useInput hook. TS think that the type is (string | onChangeType)[]. That means that string or onChangeType can be at any position in the array, while you have very fixed order.

To fix this problem you have to help it a little bit and either cast the array you return like this

return [value, onChange, reset] as [string, onChangeType, () => void];

or specify explicitly the return type of the useInput function

const useInput = (initialValue = ""): [string, onChangeType, () => void] => {...}
Sign up to request clarification or add additional context in comments.

2 Comments

i will opt by the first option. What a silly mistake of mine. Thank you so much !!
I'm sure you already know about that, but I would still mention that one suggestion how to prevent this mistakes in the future. In codesandbox/IDE you can hover variables with a pointer and see what is the type of it, because sometimes TS doesn't infer the type correctly :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.