简体   繁体   English

为什么不能将 document.createElement 的返回类型分配给仅限于 HTMLElement 的泛型?

[英]Why can't the return type of document.createElement be assigned to a generic that's restricted to HTMLElement?

I'm writing a clone(el) function that must accept an HTMLElement or subtype, and should return the exact same type that it's given.我正在编写一个clone(el) function,它必须接受HTMLElement或子类型,并且应该返回与给定的类型完全相同的类型。

How can I write this function's generic signature to be correct please?我怎样才能写出这个函数的通用签名是正确的? Thanks!谢谢!

Is no.2 where I'm going wrong, in how T is being restricted to HTMLElement ? 2 号是我哪里出错了, T是如何被限制为HTMLElement的?


To be precise, I'm writing plain JavaScript and using JSDoc in VS Code, which offers a @template definition to define a generic :准确地说,我正在编写普通的 JavaScript 并在 VS Code 中使用 JSDoc,它提供了一个@template定义来定义一个通用的

  1. define a generic T定义一个泛型T
  2. restrict T to HTMLElement or subtypes thereofT限制为HTMLElement或其子类型
  3. define both the first parameter and return type as the generic T将第一个参数和返回类型都定义为泛型T
  4. use document.createElement(tagName: string) which returns type HTMLElement使用返回类型HTMLElementdocument.createElement(tagName: string)
  5. make sure the return type of createElement is type T – I, the author, know it will be, because creating an element with the same tagName will always result in the same type of element, but createElement doesn't know this because it doesn't know specifically what value el.tagName is, it only knows it's of type string .确保createElement的返回类型是T类型——我,作者,知道它会是,因为创建一个具有相同标签名称的元素总是会产生相同类型的元素,但createElement不知道这一点,因为它不知道不知道el.tagName的具体值是什么,它只知道它的类型是string
/**
 * @template {HTMLElement} T
 * @param {T} el
 * @returns {T}
 */
function clone(el) {
    /** @type {T} */
    const newEl = document.createElement(el.tagName);
    //    ^^^^^ Error from above appears on this

    // do copying of attributes and child nodes
    return newEl;
}

I believe this to be the same result in TypeScript:我相信这与 TypeScript 中的结果相同:

function clone<T extends HTMLElement>(el: T): T {
    const newEl: T = document.createElement(el.tagName);
    // do copying of attributes and child nodes
    return newEl;
}

View on TypeScript Playground 查看 TypeScript 游乐场

However, TypeScript complains that但是,TypeScript 抱怨说

Type 'HTMLElement' is not assignable to type 'T'.类型“HTMLElement”不可分配给类型“T”。

'HTMLElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLElement'. “HTMLElement”可分配给类型“T”的约束,但“T”可以用约束“HTMLElement”的不同子类型实例化。 (2322) (2322)


The error makes sense to me, because if I rewrite it for a specific example, I get the same error, but more understandable:这个错误对我来说很有意义,因为如果我为一个特定的例子重写它,我会得到同样的错误,但更容易理解:

function clone<T extends HTMLElement>(el: T): T {
    return document.createElement('input');
}

View in TypeScript Playground 在 TypeScript 游乐场查看

Type 'HTMLInputElement' is not assignable to type 'T'.类型“HTMLInputElement”不可分配给类型“T”。

'HTMLInputElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLElement'. “HTMLInputElement”可分配给类型“T”的约束,但“T”可以用约束“HTMLElement”的不同子类型实例化。 (2322) (2322)

I've tried to read the answer in https://stackoverflow.com/a/59363875 but I feel like, in my case, I'm more restricted because document.createElement is defining its type signature as Document.createElement(tagName: string, options?: ElementCreationOptions | undefined): HTMLElement .我试图阅读https://stackoverflow.com/a/59363875中的答案,但我觉得,就我而言,我受到更多限制,因为document.createElement将其类型签名定义为Document.createElement(tagName: string, options?: ElementCreationOptions | undefined): HTMLElement

The issue you're experiencing is because document.createElement returns an HTMLElement, which is a supertype of the type parameter T. This means that the return is not guaranteed to have the same type as el, and therefore you get a type error when trying to assign it to a variable of type T.您遇到的问题是因为 document.createElement 返回一个 HTMLElement,它是类型参数 T 的超类型。这意味着不能保证返回的类型与 el 相同,因此您在尝试时会遇到类型错误将其分配给类型 T 的变量。

Your final function is fairly close, you just need to make the return as type T.您的最终 function 非常接近,您只需要将返回作为类型 T。

function clone<T extends HTMLElement>(el: T): T {
    const newEl = document.createElement(el.tagName);
    return newEl as T;
}

or more succinctly或更简洁

function clone<T extends HTMLElement>(el: T): T {
    return document.createElement(el.tagName) as T;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM