Cannot invoke an object which is possibly 'undefined'.ts(2722) - reactjs

I have a button component. I simply pass it just one onClick prop out of many optional props I've defined:
const Button = (props: ButtonProps) => {
const handleClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> = e => {
props.onClick(e);
}
return (
<StyledButton onClick={handleClick}>
{props.children}
</StyledButton>
);
};
Then I'm using it like this:
<Button onClick={(e) => {
console.log(e);
}}>Click me!</Button>
Now how can as per the error mentioned in question, object be possibly undefined? I'm clearly passing the function to it and that too as per the type definition. So, I'm passing an object to it. Simple enough!
...
onClick?: React.MouseEventHandler<HTMLElement>
...
I've added a few more strict checks in this project recently and relevant one's are:
"strictFunctionTypes": true,
"strictNullChecks": true
strict:true being already present, this error never occurred.
What's the issue here?
Update - Types added
export interface IBaseButtonProps {
type?: ButtonType;
disabled?: boolean;
size?: ButtonSize;
block?: boolean;
loading?: boolean | { delay?: number };
icon?: string;
className?: string;
prefixCls?: string;
children?: React.ReactNode;
}
export type AnchorButtonProps = {
href: string,
target?: string,
onClick: React.MouseEventHandler<HTMLElement>
} & IBaseButtonProps & Omit<React.AnchorHTMLAttributes<any>, 'type' | 'onClick'>;
export type NativeButtonProps = {
onClick: React.MouseEventHandler<HTMLElement>,
htmlType?: ButtonHTMLType
} & IBaseButtonProps & Omit<React.ButtonHTMLAttributes<any>, 'type' | 'onClick'>;
export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>
Notes:
The possible solution is to either destructure the props and add the default prop. Or use defaultProps from React. But not sure if I should require that really with Typescript.

With Typescript 3.7+, you can also use optional chaining to invoke the optional prop method:
const Button = (props: ButtonProps) => {
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e => {
props.onClick?.(e); // works
};
};
You can read more about using optional chaining - https://www.stefanjudis.com/today-i-learned/optional-chaining-helps-to-avoid-undefined-is-not-a-function-exceptions/

Now how can as per the erro mentioned in question, object be possibly undefined? [sic]
The use of Partial<T> around export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps> causes onClick to be optional. When we use Partial<T>, all the properties receive the ? and thus become optional, which means that all of them can be undefined.
There are two approached to a fix: one is to keep ButtonProps the same with onClick as optional, and to check that onClick is defined before calling it (fix 1); the other is to change ButtonProps to make onClick required (fix 2 and 3).
Fix 1: onClick remains optional
Use the ButtonProps that you already have, and then check that onClick is defined before calling it. This is what antd does in the code you linked in the comments.
const Button = (props: ButtonProps) => {
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e => {
if (props.onClick) props.onClick(e); // works
};
};
Fix 2: onClick becomes required
Change ButtonProps by not applying the Partial to the NativeButtonProps:
type ButtonProps1 = Partial<AnchorButtonProps> & NativeButtonProps;
const Button1 = (props: ButtonProps1) => {
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e => {
props.onClick(e); // works
};
};
Fix 3: onClick becomes required too
Define a RequireKeys type, which lets you to specify the keys that are not optional.
type RequireKeys<T, TNames extends keyof T> = T &
{ [P in keyof T]-?: P extends TNames ? T[P] : never };
type ButtonProps2 = RequireKeys<ButtonProps, "onClick">;
const Button2 = (props: ButtonProps2) => {
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e => {
props.onClick(e); // works
};
};
The answers to Mapped Types: removing optional modifier have more information about how I defined RequireKeys<T>.

Just a clear cut answer
if (props.onClick) props.onClick(e);
if you are defining a function props and want it to be optional, define it as,
export type ButtonProps = {
function?: () => void;
};
Explanation:
If you want to use a function as props, there may be instances when you want to pass that function (as props) and there may be other instances where you don't want to pass it.
for example,
Common Code WHERE calling the <Home/> component, say index.ts/index.js
function myfunction(){
//do something
alert("hello")
}
return (
<>
<Home myfunction={myfunction}/> //passing prop
<Home/> // not passing
</>
)
In JS, home.js
export default function Home({myfunction}) {
const const1 = "Hello World"
return (
//do something
myfunction(); //IMPORTANT line
)
}
Now, its almost equivalent in TS, home.ts
In TS, we define types of everything. So, in that case we have to define type of this function myfunction also, that we are passing.
So, for this function, we realise that,
It recieves no params, so () (empty parenthesis) is enough, if any params are there, we need to define types for them also.
Returns nothing, so return type void
export type HomeProps = {
myfunction?: () => void;
};
export default function Home({ myfunction }: HomeProps) {
const const1 = "Hello World"
return (
//do something
if (myfunction) myfunction(); //IMPORTANT line
)
}
Hint: above answer

I was creating my own custom MUI button and this is how I did it.
interface ButtonTypes {
handleClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
}
On the button component
<LoadingButton
onClick={(e) =>handleClick && handleClick(e)}
>
{"Send"}
</LoadingButton>
If you're using VScode, hover on the OnClick property and you should see the expected types.

The logical operator AND (&&) returns either the value of the first falsy operand it finds or the value of the last operand if all values are truthy (Source). Therefore, we can simply write:
(props.onClick && props.onClick(e));

The above solution is too much confusing I was getting the error
Cannot invoke an object which is possibly 'undefined
It can be easily solved by removing ? from MyProps
For Example
type CounterProps = {
value : number,
selected? : boolean,
onDelete? :(id : number) => void,
//remove ? from onDelete to solve the problem
id? : any,
}
// bottom line will shows Cannot invoke an object which is possibly undefined
<button onClick={() => this.props.onDelete(this.props.id)}>Delete</button>
A good explanation is provided here

The best variant is to use ?.call(this: unknown, ...args: any[]) or ?.apply(this: unknown, args: any[]) methods
So, lets imagine we have next declarations
type callback = ((x: number, y: number) => number) | null;
let a: callback;
let b: callback;
a = (x, y) => x + y; // it works with arrow functions
b = function (x, y) { // and normal functions
return x + y;
};
function x(cb1: callback, cb2: callback) {
console.log(cb1?.call(0, 5, 6)); // in this case you
console.log(cb2?.call(0, 5, 6)); // cant invoke cb1() or cb2()
console.log(cb1?.apply(0, [5, 6])); // but you can use call or apply
console.log(cb2?.apply(0, [5, 6])); // where first parameter can be any value
}
x(a, b); // 11 11 11 11
class C {
public f?: callback;
public setF() {
this.f = (x, y) => {
return x + y;
};
}
}
const c = new C(); // same with objects
c.setF();
console.log(c?.f?.call(c, 2, 3)); // 5

For any one who come next. Another option is to use type casting.
like:
props = props as NativeProps
In my experience I used a context who return a Partial type object and i needed to do type casting to overcome the undefined error. like:
const {setSomething} = useContext(SomePartialContext) as MyContextType

This is the syntax that helped solve this issue for me.
Different ways of getting a solution:
search.prop('onSearch')!('text' as unknown)
search.prop('onSearch')!({} as unknown)
search.prop('onSearch')!({} as any)
The key portion is: !({} as any}

Related

React and Typescript: Can I overload a function with a different number of generics?

I am writing a React functional component using typescript.To format the data before submitting it, I want to create a Form component that optionally has a format function, and a FormattedValue generic type.
However, it shouldn't allow you to use format without setting the generic type, or else you could accidentally submit the form with the wrong type.
I can't figure out how to make a function overload so that the Form function either has the generic FormattedValue set and the format parameter, or none of them.
This is a minimal example of what I tried to do:
import {Form} from "../components/Form"
type FormProps<Value> = {
submit: (data: Value) => void;
}
type FormattedFormProps<Value, FormattedValue> = {
submit: (data: FormattedValue) => void;
format: (value: Value) => FormattedValue
}
function GenericForm<V>({submit}: FormProps<V>): ReactNode;
function GenericForm<V,F>({submit, format}: FormattedFormProps<V, F>) {
return (
<Form
//...
onSubmit={(value) => {
submit(format ? format(value) : value)
}}
>
// ...
</Form>
)
}
export default GenericForm
This gives the error: This overload signature is not compatible with its implementation signature.(2394).
I want to avoid having to create a separate component for a normal form and a form with formatting.
However, it shouldn't allow you to use format without setting the generic type, or else you could accidentally submit the form with the wrong type.
I disagree with this because FormattedFormProps will ensure that the output of format is the input of submit. You don't need 2 types, you can simply use that one for both, by making format optional
type FormProps<Value, FormattedValue> = {
submit: (data: FormattedValue) => void;
format?: (value: Value) => FormattedValue
}
function GenericForm<V, F>({submit, format}: FormProps<V, F>) {
return (
<Form
//...
onSubmit={(value) => {
submit(format ? format(value) : value)
}}
>
// ...
</Form>
)
}
// infer FormProps<unknown, number>
const OK1 = GenericForm({submit: (data: number) => {}})
// infer FormProps<string, number>
const OK2 = GenericForm({submit: (data: number) => {}, format: (a: string) => 42})
// infer FormProps<string, number>
const ERROR = GenericForm({submit: (data: number) => {}, format: (a: string) => 'foo'})
// ~~~~~~
// Type '(a: string) => string' is not assignable to type '(value: string) => number'.
// Type 'string' is not assignable to type 'number'.(2322)
If you want for some reason to restrict the types that must be used for formatting and submitting on the call site you can create variants of GenericForm without re-implementing it, like so:
const FormStringToNumber = GenericForm<string, number>;

incompatible function argument in component props

I have a component that takes a list of items, known to have an ID, and a function that filters those items.
The type with an ID is a generic type of item, that all items will have.
But more specific items will include other props.
type GenericItem = {
id: string;
}
type SpecificItem = {
id: string;
someOtherProp: boolean;
}
I also have a function type, that uses the generic type to operate on.
type GenericItemFunction = (item: GenericItem) => boolean;
I then have this component that uses the GenericItem and GenericItemFunction in its props.
type CompProps = {
fn: GenericItemFunction;
items: GenericItem[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>;
When I try to use this component with the Specific type, I am getting errors saying I cannot use an implementation of GenericItemFunction because the types for item are not compatible.
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return (
<Comp
fn={filter} // error on `fn` prop
items={items}
/>
)
}
The typescript error I receive is:
Type '(item: SpecificItem) => boolean' is not assignable to type 'GenericItemFunction'.
Types of parameters 'item' and 'item' are incompatible.
Property 'someOtherProp' is missing in type 'GenericItem' but required in type 'SpecificItem'.
I guess I have two questions;
Firstly, Why is there a conflict when both types expect the id: string property?
Secondly, is there a more sane way to do something like this?
My first though was the type for item on the GenericItemFunction could be inferred from the value provided to the items prop in the App component.
But to be completely honest, I'm not sure how that would look...
My other thought was to have the Comp be a generic, but not show to use a react component that uses generics... Seems like jsx/tsx doesn't really support that syntax.
I expect something like this to throw all sorts of errors.
const Comp = <T extends GenericItem,>({ fn, items }) => <></>;
const App = () => {
return <Comp<SpecificType> />;
}
Finally, I did try this and there aren't any errors. But the downside is the type for items is inferred to be any.
type GenericItem = {
id: string;
}
type SpecificItem = {
id: string;
someOtherProp: boolean;
}
type GenericItemFunction <T> = (item: T) => boolean;
type CompProps <T extends GenericItem = any> = {
fn: GenericItemFunction<T>;
items: T[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>;
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return (
<Comp
fn={filter}
items={items}
/>
)
}
Here's a link to the playground I've been using.
https://www.typescriptlang.org/play?#code/C4TwDgpgBA4hB2EBOBLAxgSWBAtlAvFAN4BQAkCgCYBcUAzsKvAOYDcJAviSaJFAMqQ0KAGbosuAsRJRZUKrQZM2MuXQD2OCAHlgAC2QAFJOrC0ARuvUAbCAEN47Lj3DQ4iVJmw4AYgFd4NGAUdXgoAB4AFQA+KQAKFG9aSIBKAljLG3tHbhc+AGFNMGNTOgjIqAgAD2x4SjL3ZHFvKQcQWMJSMhF4WkbPCV8AoJD4KOj2OXlvOmSAbQBdJxI0UIYoQpwzKAAleyCAOh988M3ikzA6Dqg4oigegBpp3DKONPxY8OjwgHoJ7lW8HWAEEwGB4u9YqQpoD1okXrRBBBhGIvLhFlJFpM5LDgPcUNZsEh4vCcIihKJmrhIc8cAcNFpdAYkCUwOxVLIkBBgH4kGE4hyphEzoKhXIevgiGJCcguGKxaS6JLFXL5X9BSlOEA
UPDATE:
Why did you use any as default for GenericItem type? Without this I believe it should properly infer Genericitem from GenericItemFunction. – tymzap
Removing the = any for the CompProps typedef causes errors in the Comp declaration...
type CompProps <T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
}
const Comp: React.FC<CompProps> = ({ fn, items }) => <></>; // this line has the error
Generic type 'CompProps' requires 1 type argument(s).
Meaning, I still have to declare the type somewhere. Meaning I need to know the variation of the GenericItem type before I use the component.
SpecificItem is just a representation of the types that happen to overlap with the GenericItem typedef.
In most cases, the Comp wont know what type will actually be used and any doesn't give any useful information to the author.
I'm hoping for something like...
type CompProps <T extends GenericItem> = {
items: <T extends GenericItem>[];
fn: <infer from T>[];
}
But I'm not sure if this exists, or something like it.
:face_palm:
The CompProps using <T extends GenericType = any> is the correct way to do this.
type CompProps <T extends GenericItem = any> = {
items: T[];
filter: (item: T) => boolean;
}
const Comp: React.FC<CompProps> = (props) => <></>;
The magic is happening here:
const App = () => {
...
const filter = (item: SpecificItem) => item.someOtherProp;
...
}
The const Comp: React.FC<CompProps> doesn't care what the generic type is, hence = any. As long as it has at least the same shape as GenericType.
But inside the App component, where we are defining the method for the filter prop, we are declaring the SpecificItem typedef in the argument. Only that method uses the SpecificItem typedef. CompProps only cares it meets the required shape of GenericItem.
Hope that helps someone.
In most cases, the Comp wont know what type will actually be used and any doesn't give any useful information to the author.
To me, that means Comp is generic. So I would remove the = any on CompProps:
type CompProps<T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
};
...and then define Comp as generic. There's probably a way to do that with React.FC, but personally I don't use React.FC so I don't know what it is. :-) React.FC doesn't do much of anything for you, and as some people point out, amongst other things it says your component accepts children whether it does or not. I prefer to be explicit about it when my components accept children.
So I'd define Comp like this:
const Comp = <T extends GenericItem>({ fn, items }: CompProps<T>) => {
// Just to show that `fn` and `items` play together:
const example = items.map(fn);
return <>{example}</>;
};
If you wanted it to have children, you can add them easily enough. Use string if you only want to support text, or React.Node if you want to allow just about anything:
// Does allow children:
type Comp2Props<T extends GenericItem> = {
fn: GenericItemFunction<T>;
items: T[];
children: React.ReactNode;
};
const Comp2 = <T extends GenericItem>({ fn, items, children }: Comp2Props<T>) => {
// Just to show that `fn` and `items` play together:
const example = items.map(fn);
return <>{example}</>;
};
Either way, it works well in your scenario:
const App = () => {
const items: SpecificItem[] = [];
const filter = (item: SpecificItem) => item.someOtherProp;
return <>
<Comp
fn={filter}
items={items}
/>
</>;
};
And the types mean it doesn't work in places you probably don't want it to:
const App2 = () => {
const genericItems: GenericItem[] = [];
const filterGeneric = (item: GenericItem) => item.id === "example"; // Silly definition, but...
const specificItems: SpecificItem[] = [];
const filterSpecific = (item: SpecificItem) => item.someOtherProp;
return <>
{/* Desirable error, `fn` can't handle `GenericItem`s, only `SpecificItem`s */}
<Comp
fn={filterSpecific}
items={genericItems}
/>
{/* Works, `fn` handles the supertype of the items, which is fine */}
<Comp
fn={filterGeneric}
items={specificItems}
/>
{/* Desirable error because `Comp` doesn't [now] support `children` */}
<Comp
fn={filterSpecific}
items={specificItems}
>
children here
</Comp>
{/* Works because `Comp2` does support `children`*/}
<Comp2
fn={filterSpecific}
items={specificItems}
>
children here
</Comp2>
</>;
};
Playground link

How to convert value inside functional component based on generic type

I have the following component:
// ...
type StringOrNumber = string | number;
type InputProps<T extends StringOrNumber> = {
value: T;
onSubmit: (value: T) => void;
};
export default function Input<T extends StringOrNumber>(props: InputProps<T>) {
const [value, setValue] = useState(props.value.toString());
// Called on enter & blur
const submitValue = () => {
// 🔥 ts(2345): Argument of type 'string' is not assignable to parameter of type 'T'.
props.onSubmit(value);
};
// ...
return (
<input
{/* ... */}
onChange={(e) => setValue(e.target.value)}
value={value}
/>
);
}
The reason for the error above inside submitValue is quite obvious as value is always of type string here, whilst onSubmit expects a string or a number, depending on the generic T of the component.
The problem is, that I don't know how to properly work around this error. I tried using type guards but that resulted in the same errors:
function isNumber(x: StringOrNumber): x is number {
return typeof x === 'number';
}
// ...
export default function Input<T extends StringOrNumber>(props: InputProps<T>): JSX.Element {
// ...
const submitValue = () => {
if (isNumber(props.value)) {
const numberValue = parseFloat(value) || 0;
// 🔥 ts(2345): Argument of type 'number' is not assignable to parameter of type 'T'.
props.onSubmit(numberValue);
} else {
// 🔥 ts(2345): Argument of type 'string' is not assignable to parameter of type 'T'.
props.onSubmit(value);
}
};
// ...
}
I guess the issue is that the type guard is only checking the type of props.value here whilst it should somehow check what param type onSubmit actually expects.
How is this done correctly?
The problem is that extends StringOrNumber where StringOrNumber is string | number allows subtypes of string and number as well as those two types. String literal types are subtypes of string, and numeric literal types are subtypes of number. So even if you convert to string, you may not satisfy the constraint on T which could be more specific than that (the string literal type "example", for instance).
Your component can't correctly convert based on a generic type argument in the general case. So, three options for you:
A fairly pragmatic, but technically incorrect, type assertion.
Accept a conversion function in the props as well.
Use a discriminated union instead of a generic, so there are always exactly two possibilities: string and number.
A fairly pragmatic, but technically incorrect, type assertion
#1 looks like this but is, again, technically incorrect:
// Called on enter & blur
const submitValue = () => {
if (typeof props.value === "string") {
props.onSubmit(String(value) as T);
} else {
props.onSubmit((parseFloat(value) || 0) as T);
}
};
Playground link
Accept a conversion function
#2 looks like this:
type InputProps<T extends StringOrNumber> = {
value: T;
onSubmit: (value: T) => void;
convert: (value: string) => T;
};
// ...and then when calling `onSubmit`...
const submitValue = () => {
props.onSubmit(props.convert(value));
};
Playground link
(Or you could just make onSubmit accept string and do the conversion internally.)
Use a discriminated union
#3 looks like this:
type InputProps =
{
__type__: "string",
value: string;
onSubmit: (value: string) => void;
}
|
{
__type__: "number",
value: number;
onSubmit: (value: number) => void;
};
// ...and the component function wouldn't be generic anymore:
export default function Input(props: InputProps) {
// ...
// ...and then when calling `onSubmit`...
const submitValue = () => {
if (props.__type__ === "string") {
props.onSubmit(String(value));
} else {
props.onSubmit((parseFloat(value) || 0));
}
};
Playground link

Difference in typing when passing spread props vs html-style attributes

Summary:
Typescript gives an error when passing a prop through standard propName={propValue} but not when using spread props {...{propName: propValue}}
To clarify: I know how to solve this. I could add it explicitly to the IconButtonProps type; I could also add & React.HTMLAttributes<HTMLButtonElement> so I don't have to iterate all button props that I want to be able to pass through; I could also just use spread props everywhere (which is what I do 90% of the time anyway).
However, it seems like these two components should be the same. I'm looking to understand why these two supposedly equivalent ways of passing props behave differently in typescript.
Code Example
Playground Link
import React from "react";
type IconButtonProps = {
className?: string;
"aria-label": string;
};
const IconButton = ({ className, ...otherProps }: IconButtonProps) => {
return (
<button {...otherProps} className={className}>
pretend I am an icon
</button>
);
};
const ComponentWithAttributes = () => {
const handleClick = () => {
console.log("yooo");
};
return <IconButton aria-label="manage section" onClick={handleClick} />;
};
const ComponentWithSpreadProps = () => {
const handleClick = () => {
console.log("yooo");
};
return (
<IconButton aria-label="manage section" {...{ onClick: handleClick }} />
);
};
ComponentWithAttributes gives this error:
Type '{ "aria-label": string; onClick: () => void; }' is not assignable to type 'IntrinsicAttributes & IconButtonProps'.
Property 'onClick' does not exist on type 'IntrinsicAttributes & IconButtonProps'.(2322)
ComponentWithSpreadProps has no errors
This is because of the structural typing nature of typescript. You can read more in the link provided.
When you spread an object, you don't really know its properties, or if it has excess properties, as some objects properties are inherited. Typescript does not catch errors on any other properties on the spread object.
example
var x = { d: 0 };
var y: { a: number, b: string } = {
a: 0,
b: "",
...x,
d: 100
};
Typescripts will raise an error when you try to assign d in y, but will allow spreading of x.
I think the problem is the missing "onClick" in your IconButtonProps type.
I just edit to:
type IconButtonProps = { className?: string; "aria-label": string; onClick: () => void };
and the error is gone
Regards!

How to properly type define event.target.value?

I'm authoring a node package but I'm having bit of an issue with my typescript definitions. To be more specific I find the definition of event.target.value super confusing
Issue description:
I have the following event handler:
import { ChangeEvent, useState } from 'react'
type FieldEvent = ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
export const useField = <T>(input: T) => {
const [value, setValue] = useState<T>(input)
const handleChange = (event: FieldEvent) => {
const { name, value: eventValue } = event.target
// #ts-expect-error
setValue(eventValue)
}
return [input, handleChange]
}
The expression setValue(eventValue) results in the following error:
Argument of type 'string' is not assignable to parameter of type 'SetStateAction<T>'.
I was a bit surprised by this, given a lot of exported components use different event.target.value. Eg date-picker return Date type, select Object, etc.
Issue investigation
Naturally I went to check the imported ChangeEvent react exports to see if it has correct definitions, but this appears to be correct
interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
target: EventTarget & T;
}
so according to this definition it should inherit the type of the Element that was passed to the SyntheticEvent
so I followed the chain to the HTMLInputElement declaration located in node_modules/typescript/lib/lib.dom.d.ts which is where the crux of the issue lies
interface HTMLInputElement extends HTMLElement {
value: string
//... rest
}
I checked back and it appears all the native <input> elements default to string as their value type, which I guess make sense.
Solving the issue
Obviously this is not ideal, given this does not represent the event.target.value behavior in a lot of the reactjs projects that use third-party-packages (which my package is supposed to support). Consider the following codesandbox
The returned event.target.value is as you'd expect of typeof number
that leads me to the question, should I simply override the ChangeEvent with the following definition?
ChangeEvent<{ value: T, name: string } & HTMLInputElement>
or would this be considered a bad practice? Or is there some better way to go about doing this altogether?
handleChange is not match to required params.
I've tried and it worked:
export default function App() {
const [selected, setSelected] = useState(1);
const handleChange = (e: ChangeEvent<{
name?: string | undefined,
value: unknown | number
}>, child: React.ReactNode) => {
setSelected(e.target.value as number);
};
return (
<Select value={selected} onChange={handleChange}>
<MenuItem value={1}>One</MenuItem>
<MenuItem value={2}>Two</MenuItem>
<MenuItem value={3}>Three</MenuItem>
</Select>
);
}
Alright, I'm not 100% sure if this is the correct approach but it seems to work fine for my use-case, albeit the typing seems a tiny bit odd, but basically I'm overwriting the passed type argument to ChangeEvent and extending it by a union of one the HTML elements.
export type FieldEvent<T> = ChangeEvent<
{ value: T, name?: string } &
(HTMLInputElement | HtmlTextAreaElement | HTMLSelectElement)
>
This overwrites the type definition of the ChangeEvent, then you just need to create a handler function that extends the type argument
export type FieldHanderFunction<T> = (event: FieldEvent<T>) => void
so then inside my hook, it basically comes down to:
const useField<T> = (input: T) => {
const handleChange = (event: FieldEvent<T>) => {
// ...
}
}

Resources