Home > Mobile >  Why can't the return type of document.createElement be assigned to a generic that's restri
Why can't the return type of document.createElement be assigned to a generic that's restri

Time:01-04

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.

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?


To be precise, I'm writing plain JavaScript and using JSDoc in VS Code, which offers a @template definition to define a generic:

  1. define a generic T
  2. restrict T to HTMLElement or subtypes thereof
  3. define both the first parameter and return type as the generic T
  4. use document.createElement(tagName: string) which returns type HTMLElement
  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.
/**
 * @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:

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

However, TypeScript complains that

Type 'HTMLElement' is not assignable to type 'T'.

'HTMLElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLElement'. (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

Type 'HTMLInputElement' is not assignable to type 'T'.

'HTMLInputElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLElement'. (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.

CodePudding user response:

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.

Your final function is fairly close, you just need to make the return as type T.

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

or more sussienctly

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