镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

2339 Property 'style' does not exist on type 'Element'. #3263

Description

@NekR

Some example of code:

class Test {
  constructor(elem:Element) {
    elem.style.color = 'red';
  };
}

let elem = document.querySelector('#test');
let test = new Test(elem);

This produces:
2339 Property 'style' does not exist on type 'Element'.
If I change elem:Element to elem:HTMLElement then is says:
2345 Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'. Property 'accessKey' is missing in type 'Element'.

I understand what querySelector might not always return HTML or SVG elements, but why Element does not have style property?

Activity

  1. mhegazy commented on May 25, 2015

    @mhegazy
    Contributor

    I am not the best one to answer the question about the DOM sepec. We do generate these from IE's definitions. it is possible that it is an issue. Zhengbo Li (@zhengbli) would be able to comment better here.

    For your issue, i believe you need to use HTMLElement all the time; so assuming you do not use any SVG elements in this code path, casting should be safe.

    class Test {
      constructor(elem:HTMLElement) {
        elem.style.color = 'red';
      };
    }
    
    let elem = <HTMLElement>document.querySelector('#test');
    let test = new Test(elem);
  2. NekR commented on May 25, 2015

    @NekR
    Author

    Mohamed Hegazy (@mhegazy) yes, I now how to work around it right now, I was just confused what Element does not have style property. I am also not sure if it should have it, but it will be strange in a future to always cast any DOM requests (querySelector, getElementById, etc) to HTMLElement.

  3. zhengbli commented on May 26, 2015

    @zhengbli

    According to the MDN spec, Element interface doesn't have a style property, and querySelector does return Element.
    However, in the case of getElementById, we made it convenient by returning HTMLElement by default, although it is different from the spec. I don't know if we should do the same thing for other methods that don't obviously return HTMLElement over Element in most of the time, as it does feel hacky and inconsistent.

  4. NekR commented on May 26, 2015

    @NekR
    Author

    I am not saying MDN is wrong, I am pretty sure they are right, but they also say what Element have Element.onwheel :-)

    that don't obviously return HTMLElement over Element in most of the time, as it does feel hacky and inconsistent.

    Same for getElementById, it does not returns HTMLElement more often than, for example, querySelector because one can easily request inline SVG element by id.

    I do not think svg or xml elements are more frequint for querySelector, but I can agree what for XPath that might not be true.
    So, since querySelector returns Element then any developer should cast it to type they wanted (HTML, SVG, something else?). It would be good to have HTMLElement as a default for such methods, since (as far as I know :-)) HTML is default for the web (ignore xhtml here).

  5. kitsonk commented on May 26, 2015

    @kitsonk
    Contributor

    DOM Level 3 IDL for Element does not contain it.

  6. NekR commented on May 26, 2015

    @NekR
    Author

    Kitson Kelly (@kitsonk) yes, I got it. Please treat now this issue as "Return HTMLElement by default instead of Element from dom request methods (getElement(s)By*, querySelector(All), etc)"

  7. mhegazy commented on May 26, 2015

    @mhegazy
    Contributor

    Arthur Stolyar (@NekR) while i agree that an HTMLElement is more common target for querySelector/querySelectorAll, this change leaves users of SVGElements with one extra cast:

    // with the current definition
    querySelector(selectors: string): Element;
    
    // this is how you access it
    let htmlElement  = <HTMLElement>document.querySelector('#test');
    let svgElement = <SVGElement>document.querySelector('#test');
    
    // if it was changes to HTMLElement
    querySelector(selectors: string): HTMLElement;
    
    // this is how you access it
    let htmlElement = document.querySelector('#test');
    let svgElement = <SVGElement><Element>document.querySelector('#test');

    That is a breaking change though, as it will break existing calls that expect to cast to SVGElement.

  8. duanyao commented on May 28, 2015

    @duanyao

    As I suggested in issue 424, querySelector/querySelectorAll etc. can return UniversalElement by default, which is an imaginary Element that extends all known sub-interfaces of Element. Thus no casts are ever needed, no matter for HTML or SVG.

    Of cause this is not quite conformant to WebIDL of DOM specs, but I don't think there are many TS users that care about this problem.

  9. NekR commented on May 28, 2015

    @NekR
    Author

    Indeed, double casting seems weird. But it's also strange that TypeScript follows spec more than real use cases. What is a real benefit of returning Element for such methods except of being type-compatible with spec?

    UniversalElement solution seems good. I do not know if it's possible, but something like that will help.

  10. RyanCavanaugh commented on May 28, 2015

    @RyanCavanaugh
    Member

    SVG elements really do exist.

    If your JS runs over arbitrary DOM trees, it might encounter one, and we'd be wrong to act as if that were not the case.

  11. aj0strow commented on Feb 7, 2017

    @aj0strow

    Use a type guard. You need to check for null anyway.

    let elem = querySelector("#test")
    if (elem instanceof HTMLElement) {
        // elem.style
    } else {
        throw new Error("element #test not in document")
    }
  12. duanyao commented on Feb 7, 2017

    @duanyao

    SVG elements DO have 'style' property in browsers (including MS IE11 and Edge), so usually you don't have to check for its existence.

    The exception is arbitrary XML elements, including MathML in Firefox ( Safari not tested ). MathML is dying and most web developers don't mix arbitrary XML in HTML, so this is not a problem in practice.

    According to the CSSOM spec, both HTMLElement and SVGElement have style propterty (Element doesn't, however). So I suggest adding style to SVGElement, and to Element as an optional property in TS. can we reopen this issue?

  13. quangpdt commented on Jul 28, 2017

    @quangpdt

    If someone are still looking for the solution, I can suggest do the following simple solution. Becasue HTMLElement was extended from Element, so if we queried from the DOM, it's absolutely a HTMLElement, we can cast from Element to HTMLElement by using:

    (document.querySelector('ion-nav') as HTMLElement).style.color = 'red';
    

    Hope this help :)

  14. look997 commented on Sep 9, 2017

    @look997

    Quang Pham (@quangpdt)
    I have a bug:
    Type 'Element' is not assignable to type 'HTMLElement'.Property 'accessKey' is missing in type 'Element'.
    In:
    /** @type {HTMLElement} */ const enSentenceEl = /** @type {HTMLElement} */ sentenceEl.querySelector(".enSentence");

    How it's possible?
    After all, HTMLElement inherits from Element.

    When I do this:
    const enSentenceEl = /** @type {HTMLElement} */ sentenceEl.querySelector(".enSentence");
    Element type simply does not change. Still only: Element.

  15. aluanhaddad commented on Oct 2, 2017

    @aluanhaddad
    Contributor

    when you know I specific ID in your HTML corresponds to a more specialized type than that returned by the DOM APIs I find the following pattern to be very useful and that it satisfies the typechecker while making the code more self documenting

    declare global {
      interface Document {
        querySelector(s: '#test'): NodeList<Element & Extras>;
      }
    }
  16. kaziupir commented on Oct 2, 2017

    @kaziupir

    look997 (@look997) Use type assertion:

    as HTMLElement

    I don't think that comment can change type.

    Santosh Viswanatham (@devcer) querySelector returns one element, not array https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

  17. look997 commented on Oct 2, 2017

    @look997

    Michał (@kaziupir)

    look997 (@look997) Use type assertion:

    W pliku JavaScript? Tam chyba się nie da tak?
    Ja to mam w pliku js.

  18. kaziupir commented on Oct 2, 2017

    @kaziupir

    look997 (@look997) A to nie wiem, nigdy typescriptu nie używałem w pliku js. Może po prostu przejdź na używanie plików .ts. https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html

  19. Dok11 commented on Nov 8, 2017

    @Dok11

    Hi!
    I have same problem with svg func:
    image

    And I did next:

    interface SVGElementEx extends SVGElement {
    	getTotalLength(): number;
    }

    How true this desision?

  20. NekR commented on Nov 8, 2017

    @NekR
    Author

    Oleg Postoev (@Dok11) you can add that property to existing interface, i.e. just remove SVGElementEx extends .

  21. Dok11 commented on Nov 8, 2017

    @Dok11

    Arthur Stolyar (@NekR) how will I change existing interface? You talk about file \lib.es6.d.ts?
    Can you show simple example?

  22. NekR commented on Nov 8, 2017

    @NekR
    Author

    Sample in whatever place:

    interface SVGElement {
    	getTotalLength(): number;
    }
  23. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions