为什么以下代码不会导致编译时错误?
import * as React from 'react';
export class Test extends React.Component {
private _onReferenceUpdated = (ref: HTMLCanvasElement) => {
ref.width = 4; // This could throw, when ref is null
};
render(): JSX.Element {
return (
<canvas ref={this._onReferenceUpdated} />
);
}
}
ref
属性推断为
(JSX attribute) React.ClassAttributes<HTMLCanvasElement>.ref?: string | ((instance: HTMLCanvasElement | null) => void) | React.RefObject<HTMLCanvasElement> | null | undefined
这似乎是正确的(string
有点奇怪,但我想这只是针对属性的(。(ref: HTMLCanvasElement) => void
如何分配给(instance: HTMLCanvasElement | null) => void
?
答案就在你的问题中。
如您所述,ref
的类型推断为:
(JSX attribute) React.ClassAttributes<HTMLCanvasElement>.ref?: string | ((instance: HTMLCanvasElement | null) => void) | React.RefObject<HTMLCanvasElement> | null | undefined
这是一个";"联合";类型,这意味着由管道(|
(分隔的任何类型在这里都是有效的。所以ref
可以是以下任意一个:
(JSX attribute) React.ClassAttributes<HTMLCanvasElement>.ref?: string
((instance: HTMLCanvasElement | null) => void)
React.RefObject<HTMLCanvasElement>
null
undefined
好吧,您已经定义了_onReferenceUpdated
属性,并将其传递给ref
,如下所示:
private _onReferenceUpdated = (ref: HTMLCanvasElement) => {
ref.width = 4; // This could throw, when ref is null
};
因此,_onReferenceUpdated
的类型是(ref: HTMLCanvasElement) => void
,它很容易匹配我们上面有效的ref
类型之一:((instance: HTMLCanvasElement | null) => void)
。
请注意,换行括号没有意义,它们只是有助于提高可读性。还要注意的是,参数的名称是"0"并不重要;ref";在一种类型中;实例";另一方面。所有重要的是函数参数的顺序和类型,以及返回类型。
在TypeScript中,这样做是完全有效的:
let Foo = (a: string) => {}; // inferred type of Foo is "(a: string) => void"
Foo = (b: string) => {}; // no error; order and type of args and return type match
Foo = (c: number) => {}; // error! type of arguments don't match
免责声明:我只做了足够的研究,就得到了问题的概要。如果在另一个问题上有一个适当的解释,或者有更好理解的人愿意给出一个解释,请随意补充。
在Typescript 2.6中,添加了严格的函数类型设置,现在相反地检查函数参数。因此,
const f = (p: HTMLCanvasElement) => void p;
const g: (p: HTMLCanvasElement | null) => void = f;
是启用该设置时的错误。然而,遗憾的是,由于设计上的限制,例如TSX:中的ref
道具出现了问题
没有办法告诉TypeScript;ref";道具实际上是反变体
下面是bivarianceHack,换句话说,ref
的行为就像strictFunctionChecks
被禁用一样。那么,HTMLCanvasElement
是HTMLCanvasElement | null
的亚型,并且被接受。
当遵循类型定义时,这也是可见的,这导致:
type RefCallback<T> = { bivarianceHack(instance: T | null): void }["bivarianceHack"];
我觉得这很可悲(尤其是,这个破解在类型注释中不可见,至少在vscode中不可见(,欢迎任何适当的解决方案或主题更新。至少,我正在考虑一个ESLint类型脚本规则,它只是以硬编码的方式强制传递给ref
的任何函数的参数可以为null。