Should I opt for the spread operator [...] or Array.from in Typescript?

After exploring TypeScript, I encountered an issue while creating a shorthand for querySelectorAll()

export function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null {
    return [...parent.querySelectorAll(DOMElement)];
}

The code above resulted in an error saying

Type 'Element[]' is not assignable to type 'HTMLElement[]'
.

So, I made a slight modification to my code

export function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null {
    return Array.from(parent.querySelectorAll(DOMElement));
}

This change resolved the error. This led me to two questions:

  • Why did Array<HTMLElement> not work but Array<Element> worked?
  • Which method should I use - spread operator [...] or Array.from()?

Additionally

As mentioned by bogdanoff in a comment

"from docs querySelectorAll returns a non-live NodeList containing Element so Array<Element> is right type."

If that's the case, why does querySelector have no issues returning HTMLElement instead of just Element?

export function selectAll(DOMElement: string, parent = document): HTMLElement | null {
    return parent.querySelector(DOMElement);
}

Answer №1

Exploring the Mechanics Behind TypeScript Signatures

To comprehend the inner workings, we must delve into how TypeScript outlines the signatures for Array.from and querySelectorAll. Here are some excerpts:

from<T>(arrayLike: ArrayLike<T>): T[];
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;

In the case of Array.from, it signifies that, when given a type T, it will yield an array consisting of elements with type T. If no specific type is provided for T, it can vary.

Precisely defining a type for the function's return value essentially conveys: "please consider T = HTMLElement". Consequently, this implies that the argument arrayLike will possess a type of ArrayLike<HTMLElement>. By setting NodeListOf<E> to match this configuration with E = HTMLElement, which indeed extends Element, TypeScript validates the setup without any objections.

The Reasoning for TypeScript's Behavior

TS behaves this way because you have deliberately specified the type as HTMLElement for the return value, enabling TypeScript to assign specific types to E and T confidently.

Striving for Excellence

An intriguing observation is that the following code snippet triggers a TypeScript error:

function selectAll(DOMElement: string, parent = document): Array<HTMLElement>  {
  const all = parent.querySelectorAll(DOMElement);
  return Array.from(all);
}

This occurs due to the lack of information regarding the potential type for E on the line where all is defined, causing it to default to being Element. When evaluating the return statement, TS rightfully points out that Element cannot be equated to HTMLElement.

Final Thoughts

Addressing your queries:

  • Using the spread operator or Array.from => in your scenario, the spread operator is not viable as it generates an error, whereas Array.from does not.
  • Why querySelector opts for returning HTMLElement over simply Element? => hopefully, the explanations provided above shed light on this aspect.

Answer №2

After experimenting with the provided code snippet, I've come to the conclusion that Array.from transforms Element[] into HTMLElement[]

function selectAllElements(DOMElement: string, parent = document): Array<HTMLElement> | null {
    const elements : Array<HTMLElement> | null=  Array.from(parent.querySelectorAll(DOMElement));
    return elements
}


function selectAllElements2(DOMElement: string, parent = document): Array<HTMLElement> | null {
    const elements =  Array.from(parent.querySelectorAll(DOMElement));
    return elements
}

If you observe, selectAllElements2 will generate an error.

Additionally, this approach is not effective:


const arr = ["a","b"];

const setup1 = () : number[] => {
    return [...arr]
}

const setup1 = () : number[] => {
    return Array.from(arr)
}

This is because HTMLElement is essentially a subclass of Element[] itself.

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

node-ts displays an error message stating, "Unable to locate the name '__DEV__' (TS2304)."

I recently inserted __DEBUG__ into a TypeScript file within my NodeJS project. Interestingly, in VSCode, no error is displayed. However, upon running the project, I encounter an immediate error: error TS2304: Cannot find name '__DEBUG__'. I att ...

To access the value of a DOM input in an Angular component, utilize the "renderer/renderer2" method

Recently, I embarked on my journey to learn Angular. One of my goals is to retrieve data from a form and store it in a database (Firebase) using angularfire2. While going through the documentation, I noticed that there is a "setValue()" method available b ...

What is the best way to declare and initialize a global variable in a TypeScript Node application?

When using Webpack: const WebpackConfig = { // ... plugins: [ new Webpack.DefinePlugin({ __IS_DEVELOPMENT_BUILDING_MODE__: isDevelopmentBuildingMode, __IS_TESTING_BUILDING_MODE__: isTestingBuildingMode, __IS_PRODUCTION_BUILDING_MO ...

The absence of typings.json in Typescript is creating an issue

As of now, I am encountering the following error during compilation: typings.json is missing In my existing packages.json, I have included the following dependency: "devDependencies": { "typescript": "^2.6.1", ... } Do you have any suggestion ...

Using Typescript to set a custom timeout duration based on a dynamic variable within a for loop

My function includes a timeout that changes every 3 seconds: setActiveImage(promotions) { for (let i = 0; i <= promotions.length - 1; i++) { setTimeout(()=> { this.activeImage = 'http://myrul/public/Commercials/' + promo ...

The dynamic concatenation of Tailwind classes is failing to have any effect, even though the full class name is being

I'm currently using Tailwind CSS within my Next.js project and I have a common method that dynamically returns the desired background color. However, despite adding the full class name, the background color is not displaying as expected. After reading ...

React's memo and/or useCallback functions are not functioning as anticipated

Within my Home Component, there is a state called records, which I utilize to execute a records.map() and display individual RecordItem components within a table. function Home() { const [records, setRecords] = useState<Array<RecordType>>(l ...

Is it possible to integrate a personalized theme into react-dates?

Attempting to customize the styling of my react-dates DayPickerRangeController in Typescript using react-with-styles and Aphrodite. I have implemented the following code, mirroring the code found at https://github.com/airbnb/react-dates#interfaces: const ...

Initial position of the range slider in IONIC 2

I have been searching the web extensively to find a solution to this particular issue. I am working with a range slider and trying to set its default starting values, but so far, I haven't had any luck. I've checked both the official documentatio ...

Key constraints in generics: a key must belong to a distinct object type

Is there a way to enhance the safety of this function even further? Consider this object/shape: export const initialState: State = { foods: { filter: '', someForm: { name: '', age: 2, ...

TS7016: No declaration file was found for the module named 'rxjs'

I recently updated my Angular App dependencies and successfully installed them. However, I am now facing an issue with 'rxjs'. The IDE returned the following error: TS7016: Could not find a declaration file for module 'rxjs'.'C:/ ...

What is the best way to troubleshoot the TypeScript error I am encountering in my JavaScript file?

Currently experiencing a TypeScript error within a JavaScript file https://i.sstatic.net/gBzWx.png The issue is within a folder containing only one JavaScript file, and there are no Node.js or package.json files present. I have disabled the TypeScript ex ...

Exploring TypeScript: Determining the data type of an object key within the object

Apologies for the vague title, I'm struggling to articulate my problem which is probably why I can't find a solution! Let me illustrate my issue with a snippet of code: type Type<T> = { key: keyof T, doStuff: (value: T[typeof key]) =& ...

Troubleshooting the issue with mocking the useTranslation function for i18n in JEST

Currently, I am facing an issue with my react component that utilizes translations from i18next. Despite trying to create tests for it using JEST, nothing seems to be getting translated. I attempted to mock the useTranslation function as shown below: cons ...

React Navigation Browser

While developing my application, I encountered an error that I can't seem to resolve. It seems to be related to how I defined the routes in the code. Originally, the app had only one route, but after making changes to have multiple routes, I started g ...

Achieving intellisense functionality in TypeScript without the use of classes

Just dipped my toes into TypeScript, attempting to convert this basic JavaScript code to TypeScript. Here is the JavaScript code snippet: Item = {} Item.buy = function (id) {} Item.sell = function (id) {} I prefer not to use classes and would like to ut ...

The function Array.foreach is not available for type any[]

I'm encountering an issue where when I attempt to use the ".forEach" method for an array, an error message stating that the property 'forEach' does not exist on type 'any[]' is displayed. What steps can I take to resolve this probl ...

Delete a specific element from an array using a specified criteria

I'm attempting to remove a specific item from an array based on the selected option. To better understand, take a look at this code: component.html <fnd-extended-select label="Tipo Prodotto:" [(ngModel)]="landingType" name="tipoprodotto"> ...

Troubleshoot: Issue with binding data from DynamicComponentLoader in Angular 2 template

My implementation involves the utilization of DynamicComponentLoader and is based on the Angular2 API Guide. https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html The code structure I have set up looks like this: import {Page} fro ...

Tips for displaying a multi-select dropdown in the Creative Tim Angular Material Pro filter panel

I am in need of assistance with modifying the standard Creative Tim Angular Pro Material template due to my limited CSS/SCSS skills. Could someone provide examples of the necessary changes, whether it involves altering the HTML or multiple CSS files withi ...