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.
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.
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.
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).
onClick
remains optionalUse 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 }; };
onClick
becomes requiredChange 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 }; };
onClick
becomes required tooDefine 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>
.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With