Skip to content
Advertisement

‘Illegal invocation’ using input setCustomValidity and TS

I’m pretty much new to Typescript world and am converting one of the first React applications.

Here I’m trying to set custom messages on input validation by using event.target.setCustomValidity but I’m getting the ‘Illegal invocation’ error when the setCustomValidity function gets triggered.

Here there is the sandbox; To trigger the error it’s just required to update the first input.

Debugging the error, I’d think the issue stands on the declared type of event.target set as HTMLInputElement, or on the declared type of event set as a React.SyntheticEvent; Perhaps I’m using the wrong types for the event which doesn’t always have a setCustomValidity function?

To premise the code used on the sandbox works perfectly fine without Typescript

Any suggestion is highly appreciated, thank you in advance for your help!

Components code

App.tsx:

import React, { useState } from "react";
import Input from "./Input";
import "./styles.css";

export default function App() {
  // Check if value entered is a valid email. Ref: http://emailregex.com/
  const emailPattern = /^(([^<>()[]\.,;:s@"]+(.[^<>()[]\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))/;

  const initialForm = {
    username: "",
    password: ""
  };
  const [form, setForm] = useState(initialForm);

  // Listen to form inputs and updates form state respectively
  const handleChange = (event: React.SyntheticEvent) => {
    const {
      name,
      value,
      type,
      setCustomValidity
    } = event.target as HTMLInputElement;
    setForm({ ...form, [name]: value });

    if (type === "email" && !emailPattern.test(value)) {
      setCustomValidity("Please select a valid email address.");
    } else {
      setCustomValidity("");
    }
  };

  return (
    <div className="App">
      <Input
        id="username"
        type="string"
        name="username"
        label="username"
        value={form["username"]}
        onChange={handleChange}
        required={true}
      />
      <Input
        id="password"
        type="password"
        name="password"
        label="password"
        value={form["password"]}
        onChange={handleChange}
        required={true}
      />
    </div>
  );
}

Input.tsx:

import React from "react";

type PropsType = {
  id: string;
  type: string;
  name: string;
  value: string;
  label: string;
  onChange: (event: React.SyntheticEvent) => void;
  required: boolean;
};

/**
 * Reusable standard Input component.
 * @param {Object} props - Props passed from parent component containing input attributes.
 * @return - Controlled input element.
 */
const Input: React.FC<PropsType> = ({
  id,
  type,
  label,
  onChange,
  required,
  ...rest
}: PropsType) => (
  <div className="input">
    <input
      id={id}
      type={type}
      placeholder=""
      {...rest}
      onChange={(event) => onChange(event)}
      minLength={type === "password" ? 8 : undefined}
    />
    <label htmlFor={id}>
      {label}
      {required && " *"}
    </label>
  </div>
);

export default Input;

Advertisement

Answer

As @Heretic Monkey pointed out, you should use event.target.setCustomValidity rather than destructuring the method to avoid losing the context of the function when it’s called. See Why are certain function calls termed “illegal invocations” in JavaScript?.

Then, to properly type the event you may want to start by changing the onChange typing in Input.tsx.

// src/Input.tsx
type PropsType = {
    //...
    onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
};

Then update your handleChange callback accordingly in App.tsx.

// src/App.tsx
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    //...
    if (type === "email" && !emailPattern.test(value)) {
        event.target.setCustomValidity("Please select a valid email address.");
    } else {
        event.target.setCustomValidity("");
    }
}
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement