Inferring type '1' instead of number

Viewed 91

I have this code of typescript

type X = (<T extends number | string>(a: T) => (b: T) => T)

const f: X = (a: any) => (b: any) => a + b

f(1)(2)

It showing error

Argument of type '2' is not assignable to parameter of type '1'.

The inferred type in the first argument is '1' but I want it to be number with same constraints on generics string | number.

The types of arguments passed can only be either number or string i.e. constrained to string | number.

Playground

2 Answers

Would this work for your case?

type XGen<T extends string | number> = (a: T) => (b: T) => T;
type X = XGen<string> & XGen<number>;
const f: X = (a: any) => (b: any) => a + b;

f(1)(2);
f(1)('2'); // error
f('1')('2');
f([])([]); // error

The goal is to direct TypeScript to choose only between string and number (not guessing any other types that extend their union (such as 1 as in your example)). We achieved this by creating a basic generic helper type XGen and creating an intersection type of XGen<string> and XGen<number> (therefore forcing typescript to choose only between these two).

why <T extends number | string> and not just <T=number|string>?

I've just tried

type X = (<T = number | string>(a: T) => (b: T) => T)

const f: X = (a: any) => (b: any) => a + b

console.log( f(1)(2) );
console.log( f("1")("2") );

Result:

[LOG]: 3 
[LOG]: "12" 

in the same playground as you gave us and it worked just fine.

It's been a while since I last wrote any TS code, but I think when you use extends constraint, you're implying that the T will be some derived type and the only type available matching f(1) was some kind of "literal type". I mean, 1 was "upgraded" from number to literal type "1" (same mechanisms as with literal-strings-as-types etc, just with numeric-literals instead of string-literals)..

I bet you didn't really mean a X type to be a descendant of Number or descendant of String.. it's feels to me somewhat.. rare..

Related