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

ReactjsTypescript

Reactjs Problem Overview


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.

Reactjs Solutions


Solution 1 - Reactjs

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/

Solution 2 - Reactjs

> 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 https://stackoverflow.com/questions/49655419/mapped-types-removing-optional-modifier have more information about how I defined RequireKeys<T>.

Solution 3 - Reactjs

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

Solution 4 - Reactjs

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.

Solution 5 - Reactjs

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));

Solution 6 - Reactjs

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

Solution 7 - Reactjs

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

Solution 8 - Reactjs

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}

Solution 9 - Reactjs

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHalfWebDevView Question on Stackoverflow
Solution 1 - ReactjschrismarxView Answer on Stackoverflow
Solution 2 - ReactjsShaun LuttinView Answer on Stackoverflow
Solution 3 - ReactjskishoreView Answer on Stackoverflow
Solution 4 - ReactjsTittohView Answer on Stackoverflow
Solution 5 - ReactjsMukesh MauryaView Answer on Stackoverflow
Solution 6 - ReactjsVasya Krenta vkrentaView Answer on Stackoverflow
Solution 7 - ReactjsEran OrView Answer on Stackoverflow
Solution 8 - ReactjsRoger PerezView Answer on Stackoverflow
Solution 9 - ReactjsAzhar Uddin SheikhView Answer on Stackoverflow