Using TypeScript to set an HTMLElement in a web page

Currently in the process of transitioning my old JavaScript code to TypeScript. One of the components I have is a Table Of Contents JSX component that helps me navigate easily to specific headings.

I had a function that calculated the total offset needed for scrolling to a particular heading. However, after converting it to a '.tsx' file, an error popped up:

The error reads: 'Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 107 more.'

// TOC.tsx
...
const accumulateOffsetTop = ( el: HTMLElement | null, totalOffset = 0 ) => {
  while ( el ) {
    totalOffset += el.offsetTop - el.scrollTop + el.clientTop
    el = el.offsetParent // <- Error occurs here
  }
  return totalOffset
}
...

This function gets called at this location:

// TOC.tsx
...
export default function Toc(
  { headingSelector, getTitle, getDepth, ...rest }
) {
  const { throttleTime = 200, tocTitle = `Contents` } = rest
  const [headings, setHeadings] = useState( {
    titles: [],
    nodes: [],
    minDepth: 0,
    offsets: [],
  } )
  const [open, setOpen] = useState( false )
  const [active, setActive] = useState()
  const ref = useRef()
  useOnClickOutside( ref, () => setOpen( false ) )
  useEffect( () => {
    const selector = headingSelector || Array.from(
      { length: 6 }, ( _, i ) => `main > h` + ( i + 1 )
    )
    const nodes = Array.from( document.querySelectorAll( selector ) )
    const titles = nodes.map( node => ( {
      title: getTitle ? getTitle( node ) : node.innerText,
      depth: getDepth ? getDepth( node ) : Number( node.nodeName[1] ),
    } ) )
    const minDepth = Math.min( ...titles.map( h => h.depth ) )
    const startingOffsets = nodes.map( el => accumulateOffsetTop( el ) - 100 )
    setHeadings( { titles, nodes, minDepth, startingOffsets } )
  }, [headingSelector, getTitle, getDepth] )

  const scrollHandler = throttle( () => {
    const { titles, nodes } = headings
    const offsets = nodes.map( el => accumulateOffsetTop( el ) )
    const activeIndex = offsets.findIndex(
      offset => offset > window.scrollY + 120
    )
    setActive( activeIndex === -1 ? titles.length - 1 : activeIndex - 1 )
  }, throttleTime )
  useEventListener( `scroll`, scrollHandler )
...
}

Struggling with setting the correct types for the parameters passed to accumulateOffsetTop. Any suggestions?

Answer №1

One of the reasons why the error is thrown is due to the fact that el.offsetParent is of type Element | null, not specifically HTMLElement | null. The issue arises because el.offsetParent can potentially return various types of elements, including SVGElement as documented in issue#156. To address this, it is important to utilize type assertions and ensure that the returned value is indeed an HTMLElement if needed.

Answer №2

My final solution involves utilizing type assertions.

const calculateElementOffsetTop = ( element: HTMLElement, cumulativeOffset = 0 ) : number => {
  while ( element ) {
    cumulativeOffset += element.offsetTop - element.scrollTop + element.clientTop
    element = element.offsetParent as HTMLElement
  }
  return cumulativeOffset
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Deployment failure due to undetected development keys in gitignore

I have a TypeScript-coded Express server with three files for keys in the compiled and pre-compiled code: /// dev.ts - development keys const keys = { googleClientSecret: "GOOGLE_KEY", mongoURI: "mongodb+srv://MONGO_K ...

problem with arranging sequences in angular highcharts

I am facing an issue with sorting points in different series using highcharts. To illustrate my problem, consider the following example series: [ {name: 'series one', value: 5 values}, {name: 'series two', value: 10 values} ] When usin ...

Jest is unable to handle ESM local imports during resolution

I am encountering an issue with my Typescript project that contains two files, a.ts and b.ts. In a.ts, I have imported b.ts using the following syntax: import * from "./b.js" While this setup works smoothly with Typescript, Jest (using ts-jest) ...

I am puzzled as to why I keep receiving the error message "Cannot read property 'poPanel' of undefined"

CSS In my project, I am implementing a feature that displays an ordered list by looping through an array of objects and adding them on a button click. It works smoothly for adding items, but when I try to implement a remove function to delete each item, I ...

Strange behavior detected in TypeScript generic function when using a class as the generic parameter

class Class { } const f0 = <T extends typeof Class> (c:T): T => { return c } const call0 = f0 (Class) //ok const f1 = <T extends typeof Class> (c:T): T => { const a = new c() return a //TS2322: Type 'Class' is not assigna ...

Angular component name constraints - 'the selector [your component name] is not permissible'

When trying to generate a component using the Angular 6 CLI (version 6.0.7), I encountered an issue. After typing in ng g c t1-2-3-user, I received an error message stating that the selector (app-t1-2-3-user) is invalid. I wondered if there was something ...

Mongoose does not compare BCRYPT passwords that are empty

I'm currently working on incorporating bcrypt into my mongoose model using typescript. Referencing this link as a guide. However, since my project is in typescript, I'm unable to directly use the provided code. I'm confused about how they&a ...

Is there a way to install @types that are compatible with an outdated version of TypeScript?

I am currently working on a project that relies on packages such as @types/express and @types/body-parser. The problem is, the recent updates to these .d.ts files have introduced generic defaults, which now require TypeScript 2.3 or higher. Unfortunately, ...

A data type that exclusively accepts values from an enumerated list without mandating the inclusion of every possible value within the enum

Here's a code snippet I'm working with: enum Foo { a, b, c } type Bar = { [key in keyof typeof Foo]: string; } const test: Bar = { a: 'a', b: 'b' }; I'm encountering an issue where the code is complaining ...

Unable to access the redux store directly outside of the component

When trying to access my store from a classic function outside the component, I encountered an error while calling getState(): Property 'getState' does not exist on type '(initialState: any) => any' Below is the declaration and im ...

What is the best way to transpile TypeScript within the Astro framework?

Recently, I decided to dive into exploring Astro for a couple of upcoming projects. In my research, I delved into the script and typescript sections of the documentation (), as well as (). However, I found the workflow somewhat counterintuitive and struggl ...

Avoiding the ESLint error of missing return type in a React TypeScript function

My JavaScript function looks like this: export default () => ({ root: css` background-color: hotpink; margin-bottom: 1.45rem; `, }); However, ESLint is raising a complaint: Missing return type on function.eslint@typescript-eslint/explicit-m ...

What is the reason for my algorithm's inability to work with this specific number?

I'm currently working on creating an algorithm to compute the sum of prime numbers that are less than or equal to a specified number. Below is my attempt: function calculatePrimeSum(num) { // initialize an array with numbers up to the given num let ...

Optimal Approach for Redirecting Authorization

I'm currently working on setting up an authorization feature for my Angular application. Here is the detailed process I am following: First, I generate a state and code in the front end. Upon clicking the login button, the application redirects to /a ...

Angular: Smooth transitions for ngif animations - Learn how to animate ngif elements seamlessly as one element is removed from the DOM

Is there a way to delay the execution of ngIf? I have a scenario where elements in a parent component fade out and are removed from the DOM using ngIf, followed by elements from a child component coming in. However, I am facing an issue with elements overl ...

Some elements that fit the criteria of 'number | function' are not callable at all

Consider a basic function like this: export const sum = (num?: number) => { const adder = (n: number) => { if (!n) { return num; } num = (num && num + n) || n; return adder; }; return a ...

Getting object arguments from an npm script in a NodeJS and TypeScript environment can be achieved by following these steps

I'm trying to pass an object through an NPM script, like this: "update-user-roles": "ts-node user-roles.ts {PAID_USER: true, VIP: true}" My function is able to pick up the object, but it seems to be adding extra commas which is ...

callback triggering state change

This particular member function is responsible for populating a folder_structure object with fabricated data asynchronously: fake(folders_: number, progress_callback_: (progress_: number) => void = (progress_: number) => null): Promise<boolean ...

When deploying my Angular project, I am unable to access my files

I have been facing challenges while trying to deploy my web application with the frontend being Angular. The issue I am encountering is that I cannot access my JSON file located in the assets folder. Below is the function I am using to retrieve data from ...

What is the method for adding attributes to a class dynamically in TypeScript so that they can be accessed by instances?

I attempted to create a universal factory function that generates custom enum objects, but the instances were not able to retrieve the correct properties. Take a look at the preview of the code online: https://stackblitz.com/edit/typescript-rkl1zr Workin ...