在Typescript 中,为什么我可以这样做……但我不能这样做……
因为querySelector
是一个重载的泛型函数,而Event["target"]
只有一个简单类型.不能在不缩小范围(或断言)的情况下将超类型(如EventTarget
或Element
)赋给子类型(如HTMLInputElement
).现在发生的情况是,querySelector
的返回类型相当复杂,因此您实际上并没有这样做.
在使用文字字符串"input"
作为参数的specific示例中,您可以这样做,因为querySelector
具有适用的签名:
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;
这使它可以将字符串文字参数"input"
映射到HTMLInputElement
.(这也意味着您不需要类型注释,即element
、would be inferred和HTMLInputElement | null
的类型,即使您没有它,这要归功于那个重载.)
相比之下,Event["target"]
仅仅是:
readonly target: EventTarget | null;
没有什么特别的事情发生,所以如果您想将其作为子类型的实例处理,则必须使用类型断言或谓词.
(对我来说)更有趣的 case 是:
function example(selector: string) {
const element1 = document.querySelector(selector);
// ^? const element1: Element | null
const element2: HTMLInputElement | null = document.querySelector(selector);
// ^? const element2: HTMLInputElement | null
}
Playground link个
在这种情况下,我上面引用的重载不适用,因为参数只是一个字符串,而不是字符串文字,并且其中可能有任何 Select 器.这是适用于以下情况的过载:
querySelector<E extends Element = Element>(selectors: string): E | null;
对于element1
,因为我们没有提供类型参数,并且类型脚本没有任何东西可以用来推断它,所以它缺省为Element
,所以调用返回Element | null
.
然而,对于element2
英镑,它为什么会允许这样做?为什么我们没有使用类型断言或谓词(或显式类型参数)就可以将Element | null
赋给HTMLInputElement | null
?相反,TypeScrip正在从element2
‘S类型中推断类型参数.
让我们看看它是怎么做的-
element1
:
element2
:
请注意推断类型参数的不同之处,从而与返回类型不同.由于推断的类型参数,结果是HTMLINputElement | null
,因此赋值是有效的.
(For my various examples, I have all strict checks enabled, but just read past the 100s.😊)