Typescript not reading the correct type

g3mini :

I have this React hook:

import { ChangeEvent, RefObject, useEffect, useState } from "react";

type OriginalOnchange = (e: ChangeEvent<HTMLInputElement>) => void;
type OnchangeEvent = ChangeEvent<HTMLInputElement>;
export type OnChangeWrapper = (originalOnchange?: OriginalOnchange) => (onchangeEvent: OnchangeEvent) => void;

export const useTrackValidity = (
  inputRef: RefObject<HTMLInputElement | undefined>,
  value: string,
  invalidInputMessage?: string
) => {
  const [isValid, setIsValid] = useState<boolean>();

  const onChangeWrapper: OnChangeWrapper = (originalOnchange?: OriginalOnchange) => (onChangeEvent: OnchangeEvent) => {
    setIsValid(!(!!invalidInputMessage || !onChangeEvent.currentTarget.validity.valid));
    if (!!originalOnchange) {
      originalOnchange(onChangeEvent);
    }
  };

  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.setCustomValidity(invalidInputMessage || "");
      setIsValid(!(!!invalidInputMessage || !inputRef.current.validity.valid));
    }
  }, [inputRef, invalidInputMessage, value]);

  return [isValid, onChangeWrapper];
};

When I call it like this:

 const [isValid, onChangeWrapper] = useTrackValidity(inputRef, value?.toString() ?? "", invalidInputMessage);

  return (
    <StyledInput
      ref={inputRef}
      type={inputType || type}
      name={inputName || name}
      required={isRequired || required}
      onChange={onChangeWrapper(onChange)}
      onBlur={onChangeWrapper()}
      isValid={isValid}
      value={value}
      {...props}
    />
  );

I get this error:

TS2349: This expression is not callable.
  Not all constituents of type 'boolean | OnChangeWrapper' are callable.
    Type 'false' has no call signatures. TS2722: Cannot invoke an object which is possibly 'undefined'. 

whenever I call onChangeWrapper.

When I change the above to this:

const [isValid, onChangeWrapper] = useTrackValidity(inputRef, value?.toString() ?? "", invalidInputMessage) as [
    boolean | undefined,
    OnChangeWrapper
  ];

It does work, which strikes me as odd because I basically reiterated the return type.

T.J. Crowder :

TypeScript infers that the returned array will be Array<boolean|yourfunctiontype> — that is, that all elements will be the intersection of those types. boolean isn't callable, so you can't call any of the elements without some kind of guard.

To solve it, tell TypeScript that your hook function returns [boolean, yourFunctionType], specifically:

type onChangeWrapperType = /*...the function type here...*/;

// ...

export const useTrackValidity = (
  // ...
): [boolean, onChangeWrapperType] => {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−− here
    // ...
    const onChangeWrapper: onChangeWrapperType = /*...*/;
    // ...
    return [isVaild, onChangeWrapper];
};

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=11754&siteId=1