如何在 TypeScript 中正确键入元素和事件目标?

问题描述 投票:0回答:1

在 TypeScript 中为什么我可以这样做:

const element: HTMLInputElement = document.querySelector('input');

但我不能这样做:

const element: HTMLInputElement = event.target;

据我了解,

HTMLInputElement
是从
Element
返回的
document.querySelector
的子类型。

但在第二种情况下,

HTMLInputeElement
也是
EventTarget
的子类型,那么为什么第一行有效而第二行给我一个类型错误?

typescript dom
1个回答
0
投票

因为

querySelector
是一个重载的泛型函数,但
Event["target"]
只有一个简单类型。

在使用文字字符串 "input" 作为参数的

特定
情况下,您可以这样做,因为
querySelector
具有适用的签名:

querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;

这让它可以将字符串文字参数

"input"
映射到
HTMLInputElement
。 (这也意味着您不需要类型注释,即使您没有,由于该重载,
element
的类型也会被推断为 HTMLInputElement | null
。)(在您的示例中,您必须禁用严格的空检查,否则您会收到有关类型上没有 
| null
 的错误。对于我的所有示例,我都启用了所有严格检查。)

相比之下,

Event["target"]

很简单:

readonly target: EventTarget | null;
那里没有什么特别的,所以如果你想将它视为子类型的实例,你必须使用类型断言或谓词。

(对我来说)更有趣的案例是:

function example(selector: string) { const element1 = document.querySelector(selector); // ^? const element1: Element | null const element2: HTMLInputElement | null = document.querySelector(selector); // ^? const element2: HTMLInputElement | null }

游乐场链接

在这种情况下,我上面引用的重载不适用,因为参数只是一个字符串,而不是字符串文字,并且其中可以有任何选择器。这是适用的重载:

querySelector<E extends Element = Element>(selectors: string): E | null;
对于 

element1

,由于我们没有提供类型参数,并且 TypeScript 没有任何东西可用于推断它,因此它默认为 
Element
,因此调用返回 
Element | null

对于

element2

,为什么它允许呢?即使我们没有使用类型断言或谓词(或显式类型参数),为什么我们可以将 
Element | null
 分配给 
HTMLInputElement | null
?因为 TypeScript 从 
element2
 的类型推断类型参数。

让我们看看它是如何做到的‐

element1

element2

注意推断类型参数的差异。

© www.soinside.com 2019 - 2024. All rights reserved.