Can a type be designed to allow the second argument to be typed according to the type of the first argument?

In my preference, I would rather have the type of the second argument inferred from the type of the first argument instead of being explicitly specified as a type argument. This way, it can be passed without the need for explicit typing.

I typically define functions like this:

const addT = (a, b) => a + b
  • a can be string | number
  • b should have the same type as a

There are several potential solutions, but none of them automatically infer types from the arguments.

const addT = <T extends string | number>(a, b) => a + b
//                                                ^^^^^
// Operator '+' cannot be applied to types 'T' and 'T'.(2365)


addT(1, 3)
addT("", "")
type TAdd2 = {
  // overload for when both a and b are numbers
  <T extends number>(a: T, b: T): T;
  // overload for when both a and b are strings
  <T extends string>(a: T, b: T): T;
};

const add: TAdd2 = (a, b) => a + b;
//                  ^  ^
// Parameter 'a' implicitly has an 'any' type.(7006)
// Parameter 'b' implicitly has an 'any' type.(7006)

add("", "")
add(1, 2)
// This doesn't work because TAdd3 is now defaulting the generic type argument to `string`

type TAdd3<T1 extends string | number = string, T2 extends string | number = string> = (a: T1, b: T2) => (T1 extends number ? (T2 extends number ? number : never) : string);
const add3: TAdd3 = (a, b) => a + b;
add3("", "")
add3(1, 2)
//   ^
// Argument of type 'number' is not assignable to parameter of type 'string'.(2345)

const add4 = <T extends number | string>(a: T) => (b: T) => typeof a === 'string' ?  a + b : `${a}${b}`;
add4(1)(3)
//      ^
// Argument of type '3' is not assignable to parameter of type '1'.(2345),

Link to see more examples on TypeScript playground

Answer №1

In the past, I have encountered similar challenges. The issue with attempting to utilize generics is as follows:

  • When T extends number, it does not necessarily mean that T can be any number. It might actually mean that T is specifically 3, for example add<3>(3,3), which then results in 3+3 not equating to 3, thus the output is no longer T.
  • If you use T extends string | number, it holds true when executing add<3|'a'>(3, 'a'). However, here the outcome (after coercion) becomes the string '3a', which once again deviates from T

The solution I found that comes closest to addressing this difficulty is:

function add(a: string, b: string): string;
function add(a: number, b: number): number;
function add(a: any, b: any) {  
  return a+b;
}

Playground link

The usage of any in this context is simply to ensure that the function operates. TypeScript will determine which overload was invoked based on the provided parameters. From my observations, one can only call upon the first 2 signatures; using anything else should prompt an error.

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

Using the recommended Prettier plugin for TypeScript in ESLint is causing issues with the ability to use the override keyword

Software Versions: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6a0f190603041e2a5d445958445a">[email protected]</a> <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7000021504041915023 ...

What is the proper way to effectively utilize TS typings when using styled-components with withComponent()?

I am struggling to understand how to use the withComponent() method of a StyledComponent in a Typescript setting. I have a basic styled component that I want to apply to a React component. However, the Typescript compiler is flagging an issue with props no ...

Tips for creating an interface in TypeScript that prevents access to uninitialized properties of a class

Interfaces still baffle me a bit. I understand that interfaces are typically used for public properties, but I want to create an interface that will prevent access to uninitialized properties. Currently, I am able to access this.material without any errors ...

What is the reason for not receiving a transpilation error when referencing a non-exported class?

As a relatively new learner of Typescript and Javascript (with a background in Java), I have been navigating the transition from a single large Typescript file containing multiple classes to utilizing multiple classes. While I have made progress with the i ...

Ensure that only camelCase is permitted in Angular TSLint configurations, while prohibiting the use of Pascal case

In Angular, we are enforcing the use of lower-case first letter Camel case for code. For instance, if a variable is named ProductName with an uppercase first letter, it should trigger a warning or error. Is there a way to configure TSLint to only allow ca ...

Separate angular structure into various sections

I am developing a form builder using Angular dynamic form functionality. The form data is loaded from a JSON object, as shown below: jsonData: any = [ { "elementType": "textbox", "class": "col-12 col-md-4 col-sm-12", "key": "first_ ...

When Typescript calls the toString method on a Function, it produces unexpected characters like "path_1, (0, promises.writeFile)"

I'm currently attempting to convert a function into a string for transmission to a worker thread for execution. However, when imported code is included, the resulting string contains strange characters. import { HttpStatus } from '@nestjs/common& ...

What is the correct way to implement Vue.use() with TypeScript?

I am trying to incorporate the Vuetify plugin into my TypeScript project. The documentation (available at this link) suggests using Vue.use(), but in TypeScript, I encounter the following error: "error TS2345: Argument of type '{}' is not assign ...

How can you ensure in Typescript that a function parameter belongs to a specific set of enumerated values?

Consider this example enum: export enum MyEnum { a = "a", b = "b", c = "c" } Now, let's define a function type where the parameter must be one of these values. For instance, myFunction("c") is acceptabl ...

What is the proper way to specify a generic type as a parameter in TypeScript?

I have devised a basic Form class using TypeScript: class Form<FormData> { protected data: FormData; constructor(data: FormData) { this.data = data; } } To ensure the form receives specific data upon instantiation, I included a ...

A guide to specifying the Key-Callback pair types in Typescript

Here is an object containing Key-Callback pairs: const entitiesUIEvents = { newEntityButtonClick: () => { history.push("/entity-management/entities/new"); }, openEditEntityDialog: (id) => { history.push(`/entity-mana ...

What practical applications exist for preserving JSX post-transpilation of a tsx file?

While troubleshooting another issue, I decided to delve into Typescript's documentation on JSX usage. I discovered that some options involve converting JSX while others do not. I am puzzled as to why one would need to preserve JSX in transpiled file ...

Encountering an issue when attempting to establish a connection to cockroachdb using the geography datatype in type

Encountering an error while attempting to connect to Cockroach DB with a 'geography' column: Unable to connect to the database: DataTypeNotSupportedError: Data type "geography" in "Club.latlon" is not supported by "cockro ...

Improve your code quality with TypeScript's type checking capabilities

I am currently utilizing TypeScript version 1.4.1 and I have a need to import an external module (specifically "chai") while ensuring type checking compatibility. Yet, I seem to be facing a naming conflict issue with the following code snippet: /// <r ...

My Angular2+ application is encountering errors with all components and modules displaying the message "Provider for Router not found."

After adding routing to my basic app through app.routing.ts, I encountered errors in all of my test files stating that no Router is provided. To resolve the errors, I found that I can add imports: [RouterTestingModule], but is there a way to globally impo ...

Encountering an error when attempting to show user details on a webpage using Angular and Ionic with Promise functionality

On my app's AccountSettingsPage, I am fetching user data from a SQLite DB and displaying it on an Ionic page. However, I encountered the following error: Error: TypeError: Cannot read property 'name' of undefined at Object.eval [as upd ...

What is the method to insert a new <input> element after the last input field has been filled in

I recently started working on a form using StackBlitz, but I've hit a roadblock and need some guidance on how to proceed. My goal is to achieve a similar effect like the one shown in this gif: https://i.stack.imgur.com/76nsY.gif and I'd like to ...

What is the process for setting the active state for HtmlBodyElement?

I attempted to use the following method: document.querySelector('body').setActive(); However, I encountered an error: TS2339: Property 'setActive' does not exist on type 'HTMLBodyElement'. Any suggestions on how to resolve t ...

Guide for integrating CryptoJS with Angular 2 and TypeScript within a WebPack build setup

Looking for advice on integrating the CryptoJS library with Angular 2 using TypeScript? Many existing resources are outdated and assume the use of SystemJS. Can someone provide straightforward instructions for incorporating CryptoJS with Angular 2 and Type ...

Blending ASP.NET Core 2.0 Razor with Angular 4 for a Dynamic Web Experience

I am currently running an application on ASP.NET Core 2.0 with the Razor Engine (.cshtml) and I am interested in integrating Angular 4 to improve data binding from AJAX calls, moving away from traditional jQuery methods. What are the necessary steps I need ...