1

I have a component called Button which accepts the onChangeprops. Right now the type of onChange: event: React.ChangeEvent<HTMLInputElement> and working fine. I want to create a union type of expecting the following two types. I wanted to do something like this which makes this onChange generic type.

import React from 'react';

type handleChange = (value: string,checked: boolean,name: string) => void 
 
type eventHandleChange = <T extends HTMLElement>(
  event: React.ChangeEvent<T>
) => void;

type Change = handleChange | eventHandleChange;

type buttonProps ={
  onChange?: Change
}

export const Button =(Props:buttonProps) =>{
  
  //code here

}

But when I am trying to pass a function to props like this one it's giving me an error

import React from 'react';
import Button from '../button';

export const FooComponent=()=>{

  const changHandler = (event: React.ChangEvent<HTMLInputElement>)=>{
   setSelectedValue(event.targe.value)
  }

  return (
    <Button onChange={changeHandler} />
  )
}

ERROR

'(event: React.ChangeEvent<HTMLInputElement>) => void' is not assignable to type 'eventHandleChange | handleChange | undefined'.
   
Type '(event: React.ChangeEvent<HTMLInputElement>) => void' is not assignable to type 'eventHandleChange'.
 
Types of parameters 'event' and 'event' are incompatible.       
Type 'ChangeEvent<T>' is not assignable to type 'ChangeEvent<HTMLInputElement>'.         
Type 'T' is not assignable to type 'HTMLInputElement'.           
Type 'HTMLElement' is missing the following properties from type 'HTMLInputElement': accept, align, alt, autocomplete, and 49 more.

As HTMLInputElement is extends from HTMLElement T Should be generic here for all HTMLElement. I am not sure what I am missing here in implemantation.

2 Answers 2

2

What you might think of as "broader" and "narrower" get reversed when dealing with callbacks. In order to assign A to B, where A and B are both callbacks, A has to accept all of the types that B accepts. If A only accepts a specific subset of what B does, then it's not assignable to B.

Here, you have a type eventHandleChange that can handle the change on any HTMLElement. The callback which you are trying to assign to it only accepts the change event for HTMLInputElement, so it is not assignable.

One possible way to fix this is by moving the generic T higher up the chain so that we can say that buttonProps can take a very specific callback.

type handleChange = (value: string, checked: boolean, name: string) => void

type eventHandleChange<T extends HTMLElement> = (
  event: React.ChangeEvent<T>
) => void;

type Change<T extends HTMLElement> = handleChange | eventHandleChange<T>;

type buttonProps = {
  onChange?: Change<HTMLInputElement>
}

Typescript Playground Link

Sign up to request clarification or add additional context in comments.

Comments

0

In case you need a type that accepts any type of HTML Element you can use:

type AnyHTMLElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap]

If it comes from querySelector or ref you may need to add | null to the definition

Comments

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.