Increase the range of numbers without being confined to a particular number

I may be asking a question that has already been answered, but I couldn't find the solution so here goes:

The workaround I have is not ideal because if I need to add more types, it will make upgrading difficult. Is there a better way to approach this?

declare const max: <A extends number | string> (a: A) => (b: A) => A

max(2)(3)     // Argument of type '3' is not assignable to parameter of type '2'
max('a')('b') // Argument of type '"b"' is not assignable to parameter of type '"a"'

// Possible workaround but will become clumsy if possible type to extends grow

declare const maxClumsy: {
    (a: number): (b: number) => number
    (a: string): (b: string) => string
}

maxClumsy(2)(3)
maxClumsy('a')('b')

playground link

Answer №1

When it comes to handling just two types, function overloads are the best approach in my opinion. Your declaration looks good:

declare const maxClumsy: {
    (a: number): (b: number) => number
    (a: string): (b: string) => string
}

If you're worried about repeating string and number, you can dynamically construct this type. Function overloads are essentially an intersection of function types, so by using a distributive conditional type and the UnionToIntersection helper from another Q&A on Stack Overflow, we can transform the union type string | number into the desired type:

type CurriedUnion<T> = T extends any ? (a: T) => (b: T) => T : never
type UnionToIntersection<T> = (T extends any ? (a: T) => void : never) extends (a: infer U) => void ? U : never
type CurriedOverload<T> = UnionToIntersection<CurriedUnion<T>>

Usage:

// type Test = ((a: string) => (b: string) => string) & ((a: number) => (b: number) => number)
type Test = CurriedOverload<string | number>

declare const coolMax: CurriedOverload<string | number>

// OK
coolMax (2) (3)
// OK
coolMax ('a') ('b')
// error
coolMax (2) ('b')
// error
coolMax ('a') (3)

Playground Link

Please note that distributing over union types like this may produce unexpected results when the input types themselves are unions; for example, since boolean is defined as the union type true | false, this method will not work as expected in that scenario.

Answer №2

Expanding on the point raised by @matt-diamond

Just remember that when you pass in literals, it can cause issues... If the variables are more broadly typed, you won't face this problem.

If you want to avoid explicitly passing typed variables each time, you can try this approach.

type Num = number | string
 
type AsNum<T> = T extends string ? string : 
   T extends number ? number : never 

declare const max: <T extends Num>(a: T) => (b: AsNum<T>) => AsNum<T>

max(2,4)  max('2','4') // This works fine
max(2,'4') // Results in an error: Argument of type 'string' is not assignable to parameter of type 'number'
max(true,false) // Results in an error: Argument of type 'boolean' is not assignable to parameter of type 'string | number'

While it may look a bit unwieldy like your initial suggestion, it offers a solution nonetheless.

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

Tips for effectively deploying SvelteKit on a server without causing any disruptions to the user experience

After developing my information system using Sveltekit and setting up the server with Ubuntu 22.04 OS, PM2 as the process manager, and running the app on Node.js, I followed the Sveltekit documentation to deploy the app for a node adapter. Below is my svel ...

accessing deeply nested JSON objects during the process of mapping data from an API request

Currently, I am in the process of integrating JSON into Data-driven rendering using React. To achieve some of my objectives, I will need to work with Nested JSON structures. However, during the mapping of this data, I have encountered some difficulties in ...

Steps for verifying if an instance conforms to an interface

When working with PHP, we can check for a specific instance like this. Check it out here interface IInterface { } class TheClass implements IInterface { } $cls = new TheClass(); if ($cls instanceof IInterface) { echo "yes"; } I have a similar scena ...

What is the best way to incorporate a type into this animation element?

import React from 'react' import UseAnimations from 'react-useanimations' const Animation: React.FC = () => { return ( <React.Fragment> <UseAnimations animationKey="github" size={56} style={{ paddin ...

Guide to implementing ion-toggle for notifications with Ionic 2 and Angular 2

Currently, I am using a toggle icon to set the notification as active or inactive. The response is obtained from a GET call. In the GET call, the notification value is either 0 or 1, but in my TypeScript file, I am using noteValue as boolean, which means w ...

How can I store the status of checked and unchecked checkboxes in an array of objects using Angular 7?

I have a set of checkboxes with a parent-child structure, and their values are generated dynamically in a loop. When I click the submit button, I want to capture the selected or unselected values in the specified format (as shown in the commented output) ...

Unfortunately, I am unable to utilize my Async Validator as it is wrapped within the "__zone_symbol" object

I have created an asynchronous validator for passwords. export class PasswordsValidators{ static oldPasswordMatch(control: AbstractControl) : Promise<ValidationErrors> | null { return new Promise((resolve) => { if(control. ...

Angular's Recursive Issue with the Execution Order of RxJS Observables

I am currently experiencing an issue with the execution order of recursive RxJS Observables within an Angular application. Specifically, I have a service called RefreshReportService that handles the refreshing of reports. The refreshreport method is intend ...

Geolocation plugin in Ionic encountered an issue: "Geolocation provider not found"

I've been working on implementing geolocation in my ionic2 hello world project, and I successfully added the ionic plugin called "Geolocation" by following the instructions on the official website. After running these two commands: $ ionic plugin add ...

Using TypeScript to Add Items to a Sorted Set in Redis

When attempting to insert a value into a sorted set in Redis using TypeScript with code like client.ZADD('test', 10, 'test'), an error is thrown Error: Argument of type '["test", 10, "test"]' is not assigna ...

What is the proper way to send a list as a parameter in a restangular post request

Check out this code snippet I found: assignUserToProject(pid: number, selectedUsers: any, uid: number) { let instance = this; return instance.Restangular.all("configure/assign").one(pid.toString()).one(uid.toString()).post(selectedUsers); } ...

Can a standard tuple be matched with its corresponding key?

This code snippet showcases a function that can recognize that the key "banana" cannot have the value "red": type Fruits = { banana: 'yellow' | 'green' strawberry: 'red' } const fruit = <K extends keyof Fruits>(modu ...

Best Practices for TypeScript and React: How to Handle Component State for Mounted Components

One technique to avoid calling .setState() on an unmounted component is by using a private property like _isMounted to keep track of it, as discussed in a blog post. I have implemented this method as follows: class Hello extends React.PureComponent{ _isM ...

Is it possible for a TypeScript definition file to include a require statement?

Can I include a statement like this in my definition file (.d.ts)? import foo = require('some-module/bar'); I believed this would automatically convert my definition file into a module. Surprisingly, it still works for me even without strict mo ...

When a React component in TypeScript is passed as a parameter and then assigned to a variable, an error with code TS2604 may occur stating that the JSX element type does not

I am currently facing an issue with handling props of a passed React Element in my Factory. I am getting a TypeScript error that says: TS2604: JSX element type 'this.extraBlock' does not have any construct or call signatures. This is my Child co ...

Having trouble viewing the initial value in an AngularJS2 inputText field

I'm having trouble displaying the initial value in inputText. I'm unsure of what mistake I'm making, but the value should be showing up as expected. Kind regards, Alper <input type="text" value="1" [(ngModel)]="Input.VSAT_input1" name= ...

Converting JSON into Typescript class within an Angular application

As I work on my app using angular and typescript, everything is coming together smoothly except for one persistent issue. I have entity/model classes that I want to pass around in the app, with data sourced from JSON through $resource calls. Here's ...

Tips on displaying just two buttons in one line

When using *ngFor to display multiple buttons, all buttons appear in one column. I want to have only 2 buttons in a row: the green buttons in one line, and the red buttons in the next line. How can I achieve this? Here is what I have tried: <div class= ...

Using Firestore queries in your NodeJS application

Here's a query that is functioning as intended: const queryRef = firestore.collection('playlists').where('machines', 'array-contains', id) const snapshot = await queryRef.get() ... const playlist = document.data() as Pl ...

What is the best way to implement the trackBy function in Angular 14 with strict-mode enabled?

I am facing an issue while trying to use trackBy in angular 14 with strict-mode: true, Here is the code (which works perfectly fine in strict-mode: false) app.component.html: <div *ngFor="let item of items; index as i; trackBy: trackByFn"> ...