Can a default value be assigned to a generic argument in Typescript?

I'm currently creating versatile methods for use across various frontend applications. The goal is to invoke the function

.postAsync<CustomModel>('www.mysite.com',..., CustomModel);
and receive a CustomModel object as the response.

I want to establish a default value for the second parameter, making it a different model by default, but allowing for customization when necessary.

How can I define a default value for an argument of type Constructable<T>, where the Constructable interface is defined as:

interface Constructable<T> { new(params: any): T; }
    

I've attempted setting default values for an interface with different argument types, but encountered the error

Type Constructable<CustomModel> is not assignable to type Constructable<T>
. I've also tried setting T to CustomModel in the generic method and encountered the same error.

The expectation was that I wouldn't need to specify a model when calling the postAsync() method and that it would always return a CustomModel object. However, I received the error

Type Constructable<CustomModel> is not assignable to type Constructable<T>
.

Answer №1

After encountering an issue where a default needed to be returned when a model was not provided, I successfully resolved the problem by implementing a solution involving a generic object. The key fixes included:

  • Transitioning from typing the model as the Constructable interface to utilizing a union type of Constructable<T> | object.
  • Adjusting the default parameter for the model to be passed in as an Object.
  • The buildModel() function now examines the type of the model - if it's an object, it returns the provided values; if it's a constructable, it generates a new instance.

Discover how this solution is implemented within the postAsync parameters, the ResponseModel interface, and the buildModel method found in requestAsync:

interface Constructable<T> { new(params : any): T ;}
type ResponseModel<T> = Constructable<T> | Object;

export default class WebapiBase {
  static async postAsync<T = Object>(
      {
          uri = '',
          body = {},
          headers = new CustomHeaders(),
      }: PostRequestParams = {},
      // Default implementation provided here
      model: ResponseModel<T> = Object): Promise<T> {
      return this.requestAsync(model, HTTP_METHOD.POST, uri, headers, body);
  }
  private static async requestAsync<T = Object>(
      // This parameter accepts either a class or object
      model: ResponseModel<T>,
      method: HTTP_METHOD,
      uri: string,
      headers: CustomHeaders,
      body?: object): Promise<T> {
      const url = new URL(uri, window.location.origin);
      const request = this.buildRequest(url, method, headers, body);
      const bodyResponse = await fetch(request)
          .then(response => this.validate(response, request.headers))
          .then(validResponse => this.extractBody(validResponse))
          // Class instance or object is returned here
          .then(extractedBody => this.buildModel(model, extractedBody))
          .catch((error) => { throw this.handleError(error); });
      return bodyResponse;
  }

  // Conditional method creating an instance of the specified model
  // or returning the provided object if no model is given
  private static buildModel<T = Object>(
        Model: ResponseModel<T>,
        params: any,
    ): T {
        if (typeof Model === 'object') return params;
        return new Model(params);
  }
}

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

Ionic 2 Media Plugin File Status with Ionic Native

In the Ionic Native Media plugin documentation found here, it mentions that there are both static and instance members, such as status. I am looking for an example related to the file status in the media plugin. I attempted to do this: console.log(this. ...

What is the best way to organize class usage within other classes to prevent circular dependencies?

The engine class presented below utilizes two renderer classes that extend a base renderer class: import {RendererOne} from "./renderer-one"; import {RendererTwo} from "./renderer-two"; export class Engine { coordinates: number; randomProperty: ...

While using Angular CLI on GitLab CI, an error occurred indicating that the custom rule directory "codelyzer" could not be

ng lint is throwing an error on Gitlab CI stating: An unhandled exception occurred: Failed to load /builds/trade-up/trade-up/common/projects/trade-up-common/tslint.json: Could not find custom rule directory: codelyzer. The strange thing is that ng lint ru ...

When transmitting data from NodeJS, BackBlaze images may become corrupted

I have been facing a challenge in my React Native app where I am attempting to capture an image and then post it to my NodeJS server. From there, I aim to upload the image to BackBlaze after converting it into a Buffer. However, every time I successfully u ...

Exploring the capabilities of using the injectGlobal API from styled-components in a TypeScript

I've been attempting to utilize the simple injectGlobal API, but I am encountering difficulties getting it to work with TypeScript. Here is my setup in theme.tsx: import * as styledComponents from "styled-components"; import { ThemedStyledComponentsM ...

Tips for extracting the y-coordinate from a touch event using d3

I am utilizing d3.js to display data in my Ionic app. I have a touch event that allows me to move a line and retrieve the coordinates where it intersects with my chart. While I can easily obtain the x-coordinate representing the date, I am struggling to ge ...

Creating HTML elements dynamically based on the value of a prop within a React component

In my React component built using Typescript, it takes in three props: type, className, and children The main purpose of this component is to return an HTML element based on the value passed through type. Below is the code for the component: import React ...

Angular, manipulating components through class references instead of creating or destroying them

I am exploring ways to move an angular component, and I understand that it can be achieved through construction and destruction. For example, you can refer to this link: https://stackblitz.com/edit/angular-t3rxb3?file=src%2Fapp%2Fapp.component.html Howeve ...

Troubleshooting a 400 Bad Request Error when calling ValidateClientAuthentication in ASP.NET WebApi, even after using context.Validated()

I am currently facing an issue with my angularjs HTML client connected to a WebApi project. The APIs work fine when tested using POSTMAN or other REST clients. However, when I try to use browsers with my angularjs client, the browsers always initiate prefl ...

Developing Your Own Local Variable in Angular with Custom Structural Directive ngForIn

I am hoping for a clear understanding of this situation. To address the issue, I developed a custom ngForIn directive to extract the keys from an object. It functions correctly with the code provided below: import {Directive, Input, OnChanges, SimpleChan ...

Importing Angular Material modules

I've integrated the Angular Material module into my project by updating the material.module.ts file with the following imports: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatT ...

Zod: Establishing minimum and maximum values after converting a string to a numerical value

When dealing with a number or numeric string, I aim to convert it to a number and then validate it using the .min() and .max() methods. However, the results are not matching my expectations. const numberValid = z.number().or(z.string().regex(/^\d+$/). ...

Transforming JavaScript into TypeScript within an Angular 4 component class

Here is the Javascript code I currently have. I need to convert this into a component class within an Angular component. As far as I understand, the Character.prototype.placeAt() code is used to add a new method or attribute to an existing object. In this ...

Activate the Keypress event to update the input value in React upon pressing the Enter

I am facing an issue where I need to reset the value of an input using a method triggered by onPressEnter. Here is the input code: <Input type="text" placeholder="new account" onPressEnter={(event) => this.onCreateAccount(event)}> < ...

The Next.js build encountered an error - unable to locate function in next/script module

While constructing a CMS using next.js, one of the key components is media management through Cloudinary. The integration of the Cloudinary Media Library widget was successful during development using next/script. However, an error has now emerged that pre ...

I encountered an issue while trying to implement a custom pipe using the built-in pipe

My custom pipe seems to be functioning well, except for the built-in pipes not working within it. I've imported Pipe and can't figure out what could be causing the issue. Below is the code with the errors: import { Pipe, PipeTransform } from &a ...

Issue: React child components cannot be objects (received: object with keys)

Hey everyone, I could really use some help figuring out what I'm doing wrong. Here is the error message I'm receiving: Error: Objects are not valid as a React child (found: object with keys {id, title, bodyText, icon}). If you meant to render a ...

SonarLint versus SonarTS: A Comparison of Code Quality Tools

I'm feeling pretty lost when it comes to understanding the difference between SonarLint and SonarTS. I've been using SonarLint in Visual Studio, but now my client wants me to switch to the SonarTS plugin. SonarLint is for analyzing overall pr ...

Issues persist with @typescript-eslint/no-unused-vars not functioning as expected

.eslintrc.json: { "root": true, "ignorePatterns": ["projects/**/*"], "overrides": [ { "files": ["*.ts"], "extends": [ "eslint:recommended", ...

What could be causing my sinon test to time out instead of throwing an error?

Here is the code snippet being tested: function timeout(): Promise<NodeJS.Timeout> { return new Promise(resolve => setTimeout(resolve, 0)); } async function router(publish: Publish): Promise<void> => { await timeout(); publish(&ap ...