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 bestring | number
b
should have the same type asa
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),