Typescript's default generic datatypes

I am currently working with a protractor list page object. By default, this object returns instances of ElementFinder, but there is a way to customize it to return instances of ComplexType like the following:

class ComplexType {
  foo = 'foo';

  constructor(public element: ElementFinder) {}
}

const list = new List<ComplexType>({ element: ..., type: ComplexType});
expect(list.get(0).foo).toBe('foo');

Below is the definition of the page object:

import { ElementFinder } from 'protractor';

export interface ListItem<T> {
  new(element: ElementFinder): T;
}

export interface ListOptions<T> {
  element: ElementFinder;
  selector?: string;
  type?: ListItem<T>;
}

export class List<T = ElementFinder> {
  public readonly element: ElementFinder;
  private selector: string;
  private type: (element: ElementFinder) => T;

  get items() {
    return this.element.$$(this.selector);
  }

  get length() {
    return this.items.count();
  }

  constructor(options: ListOptions<T> | ElementFinder, selector?: string) {
    if (options instanceof ElementFinder) {
      this.element = options;
      this.selector = selector || 'li';
      this.type = (element: ElementFinder) => element;
    } else {
      this.element = options.element;
      this.selector = options.selector || 'li';
      this.type = (element: ElementFinder) => new options.type(element);
    }
  }

  get(index: number): T {
    return this.type(this.items.get(index));
  }
}

The issue I am facing is that TypeScript is unable to recognize that T can sometimes be an ElementFinder. Therefore, when I return an instance of ElementFinder, it raises an error indicating that the element finder does not match T.

This has left me confused. Is there a solution to this problem?

Answer №1

One issue we encounter is that the variable T within the class does not have a specific type assigned to it (even if some checks are performed, the compiler will not narrow down the possible types for T). As a result, all assignments must be valid for any conceivable type of T, and since

this.type = (element: ElementFinder) => element;
is not compatible with every type of T, it leads to an error.

An easy workaround is to use a type assertion if we are confident it's a false positive:

this.type = (element: ElementFinder) => element as any;

A more elegant solution would involve updating the type property to be mandatory and passing in the appropriate function:

class ComplexType {
    foo = 'foo';

    constructor(public element: ElementFinder) { }
}

export interface ListOptions<T> {
    element: ElementFinder;
    selector?: string;
    type: (element: ElementFinder) => T;
}

export class List<T = ElementFinder> {
    public readonly element: ElementFinder;
    private selector: string;
    private type: (element: ElementFinder) => T;

    get items() {
        return this.element.$$(this.selector);
    }

    get length() {
        return this.items.count();
    }
    public static default(element: ElementFinder, selector = 'li') :ListOptions<ElementFinder>{
        return {
            element,
            selector,
            type : (e) => e
        }
    }
    constructor(options: ListOptions<T>, selector?: string) {
        this.element = options.element;
        this.selector = options.selector || 'li';
        this.type = options.type;
    }

    get(index: number): T {
        return this.type(this.items.get(index));
    }
}
let element!: ElementFinder;

// example of complex usage
const list1 = new List<ComplexType>({ element, type: e=> new ComplexType(e) });
// example of default usage
const list2 = new List(List.default(element));

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

Ordering a list of IP addresses using Angular Material table sorting

Here is an example I am baffled by the fact that Material Table sorting does not properly arrange the table. I have created a stackblitz example to demonstrate this. Expected order - Sorting lowest IP first: "10.16.0.8" "10.16.0.16" & ...

Encountering an undefined property error when trying to access 'userService' while implementing an async validator in Angular 12

For the past few days, I've been struggling to implement async validation with no success from various tutorials and solutions! Service code- getUserIdToCheckDuplicate(userId:any):Observable<any>{ const url = ``; //url goes here return ...

Formik glitch when select value does not update properly after making changes

In my TypeScript web app, I am using Formik 2.2.9 and encountering issues with the HTML select element. My form includes the following controls: export interface CriterionEdit { id: number; name: string; description: string; isDeleted: boo ...

What are the steps involved in creating a definition file for npm?

Is there a recommended way to include a definition file (.d.ts) into an npm module as outlined in this guide? Specifically, how can I reference the node without using \\\ <reference /> due to restrictions? ...

Local font not applying styles in Tailwind CSS

I integrated the Gilroy font into my application, but I am facing issues with tailwindcss not being able to style the text properly. The font appears too thin in all elements such as paragraphs and headers. Here is the file structure for reference: https: ...

Associating function parameters with object types in TypeScript

In the conclusion of this post, I provide operational code for associating object types with a function that accepts an object containing matching properties. The code snippet I shared results in 'result' being resolved as: type result = { GE ...

Is there a way to incorporate an SSL certificate into a JavaScript POST request?

I need to send a post request to an endpoint using a SSL certificate in either typescript or javascript. This is for a project that I am currently working on with Ionic 3. ...

In the process of developing a custom Vue component library with the help of Rollup and VueJS 3

My goal is to develop a custom Vue component library using rollup and Vue.js. The process went smoothly with Vue2, but I encountered issues parsing CSS files with Vue3. To address this, I updated the dependencies in the package.json file. package.json { ...

Is it possible for NodeJS streams to store objects in a queue if there is no downstream pipe attached?

Is it possible for nodejs streams to queue objects natively before piping them to a Writable stream? Part 2: After calling super.push(null), I am unable to process items any further. Is there a way to restart a stream once super.push(null) has been called ...

Can you set up a mechanism to receive notifications for changes in an array variable in Angular?

I'm exploring methods to delay an HTTP request until the user stops interacting. I am considering using the debounceTime() operator from RxJs, but I need this to be triggered by changes in an array that I have defined. Here is the scenario: export c ...

Utilizing a powerful combination of Angular 5, PrimeNG charts, Spring Boot, and JHipster

I am facing an issue with creating charts using PrimeNG. The main challenge I'm encountering is the conversion of data from a REST API in Angular 5 (TypeScript) and retrieving the list of measurements from the API. I have an endpoint that returns my m ...

What is the proper way to initialize a function that interacts with the redux state?

Imagine a scenario where I have a function that retrieves a filepath from the state based on the filename provided as an input parameter. If the filepath does not exist in the state, it then fetches the filepath from a server URL. const getFilepath = (stat ...

The declaration file for the module 'vue-html-to-paper' was not located

Struggling to make a sample project work with HTML to PDF, but encountering an error message stating: Could not find a declaration file for module 'vue-html-to-paper' Even though it resides in my node_modules index.js import Vue from 'vue& ...

Example TypeScript code: Use the following function in Angular 5 to calculate the total by summing up the subtotals. This function multiplies the price by the quantity

I have a table shown in the image. I am looking to create a function that calculates price* quantity = subtotal for each row, and then sum up all the subtotals to get the total amount with Total=Sum(Subtotal). https://i.stack.imgur.com/4JjfL.png This is ...

classes_1.Individual is not a callable

I am facing some difficulties with imports and exports in my self-made TypeScript project. Within the "classes" folder, I have individual files for each class that export them. To simplify usage in code, I created an "index.ts" file that imports all class ...

Retrieving Data from Vuetify Component within vue 3

Currently, I am in the process of creating my own wrapper for Vuetify components to eliminate the need to repeatedly define the same props in each component. For example, I aim to develop a custom TextField with defaultProps while still being able to accep ...

What steps are involved in launching an outdated Angular project?

Tasked with reviving an old Angular client in my company, I found myself grappling with outdated files and missing configurations. The lack of package.json, package-lock.json, and angular.json added to the confusion, while the presence of node modules in t ...

What is the best method for accessing the properties of a JavaScript object based on input from a textbox?

Just starting out with angular and having trouble generating or updating a table based on text boxes. The schema includes country, sales, and profit fields. There are two text boxes for the x-axis and y-axis inputs. The table should dynamically update when ...

I'm experiencing issues with my TypeScript compiler within my Next.js v14 project

I am working on a project using next.js version 14 and typescript v5. After installing these dependencies, I have noticed that the typescript compiler is not detecting errors related to types as expected. For example, when defining props for a component ...

Tips for implementing md-icon in combination with md-autocomplete in Angular Material Design:

How can I include an md-icon in an md-autocomplete field? <md-autocomplete md-selected-item="ctrl.selectedItem" md-search-text-change="ctrl.searchTextChange(ctrl.searchText)" md-search-text="ctrl.searchText" md-items="it ...