Typescript complains that react component optional prop could possibly be undefined

Viewed 1180

I have a simple React component like this:

interface ComponentProps {
    onClick?: () => void;
}

const Component = (props: ComponentProps) => {
    if (!props.onClick) {
        return <></>;
    }

    return (
        // Typescript complains: Cannot invoke an object which is possibly 'undefined'.ts(2722)
        <button onClick={() => props.onClick()}>  
            test
        </button>
    );
};

Typescript complains props.onClick could be undefined, even if after the check.

Could anyone help me to understand why props.onClick can still possibly be undefined?

4 Answers

The problem is that your props object is mutable and TypeScript assumes that it can be modified between your !props.onClick check and the execution of the callback.

To fix your issue, you can destructure the props object and use the onClick attribute as a scoped variable:

const Component = ({onClick}: ComponentProps) => {
    if (!onClick) {
        return <></>;
    }

    return (
        <button onClick={() => onClick()}>  
            test
        </button>
    );
};

This is because you are using the optional property operator ? into the ComponentProps interface definition.

I see two ways to solve this issue:

The first one is to change the optional property definition to mandatory removing the ? optional property operator, so you don't need to validate if the onClick function is passed to the component.

interface ComponentProps {
   onClick: () => void;
}
const Component = ({onClick}) => (
   <button onClick={onClick}>
       test
   </button> 
)

l The second one is to create a handler function in order to validate the optional onClick function.

inteface ComponentProps {
    onClick?: () => void;
}
const Component = ({onClick}: ComponentProps) => (
   <button onClick={() => {
       if(onClick) onClick();
   }}>  
       test
   </button>
)

Bonus. You can use spread syntax and ternary operator together to validate optional properties passed to the React component.

interface ComponentProps {
   onClick?: () => void;
}
const Component = ({onClick}: ComponentProps) => (
   <button {...(onClick ? {onClick} : {})}>test</button>
)

Why don't you try

interface ComponentProps {
    onClick?: () => void;
}

const Component = ({ onClick = () => {}}: ComponentProps) => {


    return (
      
        <button onClick={() => props.onClick()}>  
            test
        </button>
    );
};
Related