javascriptreactjstypescripttypeshtmlcollection

Property 'yPos' does not exist on type 'Element'


I'm refactoring an old project and converting it from javascript to typescript. In one of my functions I am getting the position of an element using yPos (yPos seems to be enabled only after the element is dragged), the intent is to drag it to another position and drop it, but when I try to get the property yPos I get this error:

Property 'yPos' does not exist on type 'Element'.ts(2339)

I receive a HTMLCollection with 12 nodes (all div) as a parameter, loop over it and get the yPos of the actual element:

const whereAmI = (nodes: HTMLDivElement) => {

   Array.from(nodes.children).forEach((el, i) => {
      console.log('el.yPos: ', el.yPos)
   })
}

In the original code I was using a for loop:

   for (let i = 0; i < nodes.children.length; i++) {
      const el = nodes.children[i]
      console.log('el.yPos: ', el.yPos)
   }

From everything I've read I imagine that maybe I shouldn't be setting nodes: HTMLDivElement, but instead using another type, but I couldn't figure out which one. I also think that maybe I should cast el with something else, but I couldn't find anything that fits what I need.


Solution

  • The yPos looks like some non-standard property that is set on the nodes directly by not attached code.

    In order to make TypeScript aware of that, you can explicitly declare these are HTMLDivElements with the additional property:

    type DivWithPosition = HTMLDivElement & { yPos: number }
    
    const elWithPosition = el as DivWithPosition
    
    console.log('elWithPosition.yPos: ', elWithPosition.yPos)
    

    Please be aware that such as casts are somewhat an escape-hatch and basically turn off type checking by trusting the declaration.

    Sometimes TypeScript won't accept such casts and will ask to first cast to unknown like that:

    const elWithPosition = el as unknown as DivWithPosition
    

    Suggestion: The standard data-attribute syntax might be a better pick to store additional data on HTML elements instead.