无法调用可能是“未定义”的对象。ts(2722)
Posted
技术标签:
【中文标题】无法调用可能是“未定义”的对象。ts(2722)【英文标题】:Cannot invoke an object which is possibly 'undefined'.ts(2722) 【发布时间】:2019-11-16 17:33:39 【问题描述】:我有一个按钮组件。我只是在我定义的许多可选道具中只传递了一个 onClick
道具:
const Button = (props: ButtonProps) =>
const handleClick: React.MouseEventHandler<htmlButtonElement | HTMLAnchorElement> = e =>
props.onClick(e);
return (
<StyledButton onClick=handleClick>
props.children
</StyledButton>
);
;
那我就是这样用的:
<Button onClick=(e) =>
console.log(e);
>Click me!</Button>
现在,根据所提到的错误,对象怎么可能是未定义的?我清楚地将函数传递给它,并且根据类型定义也是如此。所以,我将一个对象传递给它。很简单!
...
onClick?: React.MouseEventHandler<HTMLElement>
...
我最近在这个项目中增加了一些更严格的检查,相关的是:
"strictFunctionTypes": true,
"strictNullChecks": true
strict:true
已经存在,这个错误从未发生过。
这里有什么问题?
更新 - 添加类型
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>
注意事项:
可能的解决方案是解构道具并添加默认道具。或者使用来自 React 的 defaultProps
。但不确定我是否应该对 Typescript 真正要求。
【问题讨论】:
onClick?: React.MouseEventHandler<HTMLElement>
表示onClick
可以未定义。
因为它是可选的?
删除?
以使onClick
成为必需。如果它是可选的,它可以定义为未定义
因为我无法从onClick
中删除?
,所以将defaultProps 与打字稿一起添加是个好主意吗?看来我现在必须这样做了。但是我读过很多文章来处理 defaultProps
只用 TS
去掉可选标记后还是一样的人
【参考方案1】:
现在,根据所提到的错误,对象怎么可能是未定义的? [原文]
在export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>
周围使用Partial<T>
导致onClick
是可选的。当我们使用Partial<T>
时,所有的属性都会接收到?
,从而成为可选的,这意味着它们都可以是未定义的。
有两种方法可以解决:一种是保持ButtonProps
与onClick
相同,作为可选,并在调用之前检查onClick
是否已定义(修复1);另一种是更改ButtonProps
以使onClick
成为必需(修复2 和3)。
修复 1:onClick
仍然是可选的
使用您已有的ButtonProps
,然后在调用它之前检查onClick
是否已定义。这是您在 cmets 中链接的代码中的 what antd does。
const Button = (props: ButtonProps) =>
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e =>
if (props.onClick) props.onClick(e); // works
;
;
修复 2:onClick
成为必需项
通过不将Partial
应用于NativeButtonProps
来更改ButtonProps
:
type ButtonProps1 = Partial<AnchorButtonProps> & NativeButtonProps;
const Button1 = (props: ButtonProps1) =>
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e =>
props.onClick(e); // works
;
;
修复 3:onClick
也成为必需项
定义一个RequireKeys
类型,让您可以指定非可选的键。
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
;
;
Mapped Types: removing optional modifier 的答案有更多关于我如何定义RequireKeys<T>
的信息。
【讨论】:
对 Typescript 非常陌生。因此,从这里的生产级代码逐步了解 - github.com/ant-design/ant-design/blob/master/components/button/… 他们使用相同的方式。会不会是版本问题? 如果您看到他们没有在 Button 的类组件中为onClick
指定任何 defaultProps
加上这一切都很好。但不知何故,我觉得仅仅因为有道具类型就变得复杂了。重要的是,使用上述任何一种方法都会让我在 Button props 本身上出现错误,以便分配。 Type ' children?: ReactNode; ' is missing the following properties from type ' href: never; target: never; onClick: (event: MouseEvent<HTMLElement, MouseEvent>) => void; type: never; disabled: never; size: never; block: never; loading: never; icon: never; className: never; ... 266 more ...; value: never; ': href, target, onClick, type, and 272 more.ts(2322)
如果我将React.FC
的类型引入到上面的Button
组件中,这将不起作用
您链接的 antd 代码确保在调用 onClick
之前定义了 onClick
:github.com/ant-design/ant-design/blob/master/components/button/… 我已经更新了我的答案以表明这是一个可能的解决方案。【参考方案2】:
只是一个明确的答案
if (props.onClick) props.onClick(e);
如果你正在定义一个函数 props 并希望它是可选的,那么将它定义为,
export type ButtonProps =
function?: () => void;
;
解释: 如果你想使用一个函数作为 props,可能会有你想要传递该函数(作为 props)的实例,也可能存在你不想传递它的其他实例。
例如,
Common Code WHERE 调用
<Home/>
组件,例如 index.ts/index.js
function myfunction()
//do something
alert("hello")
return (
<>
<Home myfunction=myfunction/> //passing prop
<Home/> // not passing
</>
)
在 JS 中,home.js
export default function Home(myfunction)
const const1 = "Hello World"
return (
//do something
myfunction(); //IMPORTANT line
)
现在,它几乎等同于 TS,home.ts
在 TS 中,我们定义了所有事物的类型。所以,在这种情况下,我们还必须定义这个函数的类型myfunction
,我们正在传递。
所以,对于这个函数,我们意识到,
它不接收任何参数,所以()
(空括号)就足够了,如果有任何参数,我们也需要为它们定义类型。
什么都不返回,所以返回类型void
export type HomeProps =
myfunction?: () => void;
;
export default function Home( myfunction : HomeProps)
const const1 = "Hello World"
return (
//do something
if (myfunction) myfunction(); //IMPORTANT line
)
提示:以上答案
【讨论】:
【参考方案3】:最好的变体是使用?.call(this: unknown, ...args: any[])
或?.apply(this: unknown, args: any[])
方法
所以,假设我们有下一个声明
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
【讨论】:
【参考方案4】: (props.onClick && props.onClick(e));
【讨论】:
虽然这个代码块可能会回答这个问题,但最好能稍微解释一下为什么会这样。【参考方案5】:使用 Typescript 3.7+,您还可以使用可选链来调用可选的 prop 方法:
const Button = (props: ButtonProps) =>
const handleClick: React.MouseEventHandler<
HTMLButtonElement | HTMLAnchorElement
> = e =>
props.onClick?.(e); // works
;
;
您可以阅读有关使用可选链接的更多信息 - https://www.stefanjudis.com/today-i-learned/optional-chaining-helps-to-avoid-undefined-is-not-a-function-exceptions/
【讨论】:
这比例外的答案好得多!【参考方案6】:对于接下来的任何人。另一种选择是使用类型转换。 喜欢:
props = props as NativeProps
根据我的经验,我使用了一个返回 Partial 类型对象的上下文,我需要进行类型转换来克服未定义的错误。喜欢:
const setSomething = useContext(SomePartialContext) as MyContextType
【讨论】:
【参考方案7】:这是帮助我解决此问题的语法。
获得解决方案的不同方法:
search.prop('onSearch')!('text' as unknown)
search.prop('onSearch')!( as unknown)
search.prop('onSearch')!( as any)
关键部分是:!( as any
【讨论】:
以上是关于无法调用可能是“未定义”的对象。ts(2722)的主要内容,如果未能解决你的问题,请参考以下文章
即使在未定义检查之后,打字稿“错误 TS2532:对象可能是‘未定义’”
在类型保护错误 TS2532 之后,Typescript 对象可能未定义