'extends number but do not restrict to a specific number

Probably been asked and answered but I didn't find the answer - so I ask.

The workaround is not great as if I want to include more type it will not be as easy to upgrade. Any better way of doing it ?

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



Solution 1:[1]

Following up on the comment from @matt-diamond

Keep in mind that you're passing in literals here... if the input variables are typed more broadly, you won't have an issue.

If you don't want to have to pass explicitly typed variables each time, you can use this method.

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') // OK
max(2,'4') // Argument of type 'string' is not assignable to parameter of type 'number'
max(true,false) // Argument of type 'boolean' is not assignable to parameter of type 'string | number'

Although it does look just as "clumsy" as your working suggestion, if not more.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1