TLDR:!HTMLElement 和 HTMLElement! 实际上是同一个注解。
const el = /** @type {HTMLElement!} */ (document.getElementById("abc"));
是一种类型转换。它说document.getElementById("abc") 是一个 HTMLElement,而不是 null。注释可能已添加,因为 getElementById 返回 HTMLElement 或 null。
这一行的作者应该 100% 确信存在 id 为 abc 的元素,因为在进行此转换后,闭包编译器会将这种类型视为 HTMLElement,而不是可能的 @ 987654329@或null。
用于强制转换的类型遵循与强制转换之外的类型相同的规则,因此为了直接演示如何使用这些类型,以下示例不使用强制转换。
正如 OP 所说,在 Closure Compiler 中,感叹号表示类型必须存在,并且不能是 null。此外,问号表示它可能是类型,也可能是null。
以下面的代码为例:
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// @formatting pretty_print
// ==/ClosureCompiler==
const body = document.querySelector('body');
const /** @export {Element!} */ n1 = body;
const /** @export {!Element} */ n2 = body;
const /** @export {Element} */ n3 = null;
const /** @export {?Element} */ n4 = null;
const /** @export {Element?} */ n5 = null;
console.log({
n1,
n2,
n3,
n4,
})
还有run it through the Closure Compiler,这是编译器生成的警告:
JSC_TYPE_MISMATCH: initializing variable
found : (Element|null)
required: Element at line 3 character 37
const /** @export {Element!} */ n1 = body;
^
JSC_TYPE_MISMATCH: initializing variable
found : (Element|null)
required: Element at line 4 character 37
const /** @export {!Element} */ n2 = body;
^
注意body 由document.querySelector 返回,其类型为{?Element} ,我们已经说过编译器可以理解为Element 或null,也就是Element|null。这个例子表明,符号也可以表示为?Element或Element?。