Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 361
HalfWebDev Avatar asked Jul 06 '19 12:07

HalfWebDev


People also ask

How do I fix object is possibly undefined typescript?

The "Object is possibly 'undefined'" error occurs when we try to access a property on an object that may have a value of undefined . To solve the error, use the optional chaining operator or a type guard to make sure the reference is not undefined before accessing properties.

How do you invoke an object that is undefined?

The error "Cannot invoke an object which is possibly 'undefined'" occurs when we try to invoke a function property that could be undefined , e.g. is marked as optional. To solve the error, use the optional chaining operator (?.), e.g. employee.


2 Answers

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>.

like image 150
Shaun Luttin Avatar answered Sep 20 '22 12:09

Shaun Luttin


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/

like image 26
chrismarx Avatar answered Sep 23 '22 12:09

chrismarx