Overloading Typescript functions based on the interface's generic parameter

How can a method overload with an optional parameter be declared if a class generic is of a certain type? The objective in the following example is to create a class where the run method only requires an argument if the class's generic T is a number. My current implementation is not working as expected: it seems that the T in the run declaration is being interpreted as a different generic than the one specified for the class Test. Is there a way to achieve this functionality?


class Test<T extends string | number> {
  run<T extends number>(a: number): void
  run<T extends string>(a?: number): void
  run(a: number): void {
    console.log(a)
  }
}

const a = new Test<number>();
a.run() // This should trigger an error, since a's generic type is number and therefore its run method should require an argument.

const b = new Test<string>();
b.run() // This is valid, given that b's generic type is a string.

Answer №1

If you're looking for a smart method choice, consider using a conditionally-typed rest parameter:

Check out this example on the TS Playground

class Test <T extends string | number> {
  run (...args: T extends number ? [a: number] : [a?: number]): void {
    const [a] = args;
    console.log(a); // number | undefined
  }
}

const a = new Test<number>();
a.run(42); // ok
a.run(); /*
  ^^^^^
Expected 1 arguments, but got 0.(2554) */

const b = new Test<string>();
b.run(42); // ok
b.run(); // ok

Answer №2

I've been exploring different approaches to solve this using a single class, but I'm uncertain if it's achievable. It seems logical, as having the same function invoked in multiple ways can be quite confusing.

One potential solution is to utilize an interface, a parent class, and several child classes. This method would enable you to choose between them while remaining DRY at the same time. Here's my perspective on tackling this issue.

// define an interface with an optional parameter "a"
interface ITest {
  run(a?: number): void;
}

// create a superclass to manage the "run" function process.
// this class includes a function with a required parameter
abstract class AbstractTest<T extends string|number> {
  run(a: T) {
    console.log('executed with param: ', a);
  }
}

// Define two subclasses, one for running without a parameter and the other with a parameter.
// Both classes implement the interface and call the parent function "run".

// this class does not extend the "run" function since it mirrors the parent function
class TestNumber<T extends number> extends AbstractTest<T> implements ITest {}

// this class extends the "run" function as it needs to handle cases where no parameters are passed
// I've assigned a default value here, which can vary based on your requirements.
class TestString<T extends string> extends AbstractTest<T> implements ITest {
  public static readonly DEFAULT_VALUE = 'some value';

  run(): void {
    super.run(TestString.DEFAULT_VALUE as T)
  }
}

// instantiate new objects based on the need.
const a = new TestNumber<number>();
const b = new TestString<string>();

// calling this function works when passing a parameter
a.run(42);
// this call won't work and will throw an error
a.run()

// this function call works perfectly without an argument
b.run()

I've added comments to clarify the modifications made. here is a typescript playground showcasing the functioning code

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

What are the steps to restrict a user from accessing a specific website?

In my Vue.js project, I've implemented a function that hides a specific menu item for users with insufficient permissions: <a :href="href" @click="navigate" v-if="hideMenuItem()"> // some code </a> hideMe ...

Similar to the getState() function in react-redux, ngrx provides a similar method in Angular 6 with ngrx 6

Recently, I developed an application with react and redux where I used the getState() method to retrieve the state of the store and extract a specific slice using destructuring. Here's an example: const { user } = getState(); Now, I am transitioning ...

Limit the usage of typescript to ensure that references to properties of 'any' object are verified

I'm facing a situation where I have a JS object called myObject, and it is of type any. Unfortunately, I cannot change the type as it's coming from a library. The issue I encounter is that when trying to access a property from myObject, TypeScri ...

What is the correct way to effectively integrate react-hook-form with redux and typescript?

After tirelessly searching for a comprehensive guide that could demonstrate all these requirements in one example, I eventually resorted to brute force to make something functional. However, I am well aware that this approach is not the correct way to achi ...

Tips for fixing the TS2345 compilation error when working with React

Attempting to implement the setState method in React has resulted in a compile error. Any solutions to this issue would be greatly appreciated. Frontend: react/typescript articleApi.tsx import axios from 'axios'; import {Article} from '../ ...

Using TypeScript controllers to inject $scope

Currently, I am in the process of creating my initial typescript controller and encountering a slight challenge in comprehending how to utilize $scope effectively in order to reference elements within various code blocks. Below is the relevant snippet of c ...

Is there a way to retrieve the value of bindings in the component controller using Angular 1.5 and Typescript?

In my quest to develop a versatile left-hand menu component that can dynamically display different menu structures based on input strings, I stumbled upon an interesting challenge. By binding a string to the <left-hand-menu-component> element like so ...

Angular 7 introduces updates to the way lists are ordered

I am facing an issue with my code that calls an API for each object in a list called "titles" and then adds the object to another list named "groupDocs". However, due to the asynchronous nature of the API response, the order of objects in the "groupDocs" l ...

Issues arise when trying to update the modelValue in unit tests for Vue3 Composition API

Recently delving into Vue, I am currently engaged in writing unit tests for a search component incorporated in my project. Basically, when the user inputs text in the search field, a small X icon emerges on the right side of the input box. Clicking this X ...

Utilizing absolute path imports in Vite originating from the src directory

What are the necessary changes to configuration files in a react + ts + vite project to allow for imports like this: import x from 'src/components/x' Currently, with the default setup, we encounter the following error: Failed to resolve import ...

The function did not return a Promise or value as expected when using async and await

    I have been working on implementing this code structure for my cloud functions using httpRequest. It has worked seamlessly with those httpRequest functions in the past. However, I recently encountered an error when trying to use it with the OnWrite ...

Why do rows in the React-bootstrap grid layout overlap when the screen is resized?

I am working on a simple layout structure with 2 rows: Row 1 consists of 2 columns Row 2 consists of 1 column The goal is to have both rows expand in width and avoid vertical scrolling of the page. However, when resizing the screen, I want the columns of ...

Return the reference to an injected service class from the location where it was implemented

Is it feasible to return a reference from a component class with a custom interface implemented to the injected service class in my Angular 6 project? Here is an example of what I am aiming for. ServiceClass @Injectable() export class MyService { co ...

Resolve the clash between Jest and Cypress within a React application developed using TypeScript

Encountering a conflict in the React app after installing Cypress with TypeScript. Despite trying to resolve it using GitHub solutions, the issue persists. I am sharing all configuration files in hopes that someone can identify the problem. cypress/tsconfi ...

What is causing this issue in TypeScript version 4.8?

After updating to TypeScript 4.8 in VSCode, I have encountered an error in one of my React projects that was not present before. Strangely, this error does not prevent the code from compiling successfully when building the project. It's challenging to ...

Angular is throwing an error about an undefined JSON object, even though I am able to access it

I have searched extensively for a solution to my error, but I couldn't find anything that matches exactly. I have tried solutions for similar errors, but they have not worked either. The JSON data is structured like this: [ { "somedata": "value ...

The module 'module://graphql/language/parser.js' could not be resolved

I'm facing an issue while creating a React Native TypeScript project on Snack Expo. Even though I have added graphql to the package.json and included the types file, I keep getting this error : Device: (1:8434) Unable to resolve module 'module:/ ...

In DynamoDB, when using Number in KeyConditionExpression, it is interpreted as a Map which can lead to unexpected

Setting In my TypeScript CDK project, I am dealing with the QueryInput-Object. The code snippet below shows how it's being used: const params: QueryInput = { TableName: criticalMessagesTableName, ProjectionExpression: 'message', ...

Unique TypeScript code snippets tailored for VSCode

Is it possible to create detailed custom user snippets in VS Code for TypeScript functions such as: someArray.forEach((val: getTypeFromArrayOnTheFly){ } I was able to create a simple snippet, but I am unsure how to make it appear after typing an array na ...

Implementing multiple filters for object arrays in Angular 8

On my current project, I am interested in implementing multiple filters. The filters I want to use include price range, type, and uploaded date. For uploaded date, I have a checkbox with options for Today, Last 2 days, Last 7 days, and Any. When it come ...