Overload generic functions with the first generic type not in parameters

90 Views Asked by At

I got a problem when using generic overload functions like below (playground).

The generic type T1 is not used in the parameters (just used in return type), so when I try to use overload #2, I have to provide all the types like f<string, string>('123') which sometimes is not possible. Is there a way to let f<string>('123') match overload #2?

function f<T1, T2>(t2: T2): T1
function f<T1>(): T1

function f<T1, T2>(t2?: T2): T1 | void {
}

f<string, string>('123') // ok
f<string>()              // ok
f<string>('123')         // error here, can we fix it by not using f<string, string>('123') ?

function f<T1, T2>(t2: T2): T1
function f<T1>(): T1

function f<T1, T2>(t2?: T2): T1 | void {
}

f < string, {id: number}>({id: 123})  // ok
f<string>()                           // ok
f<string>({id:123})                   // typescript complain here can we fix it by not using f<string, string>({id:123}) ?

playground

2

There are 2 best solutions below

1
On

You can use a default for the type parameter, if you want T2 to be the same as T1 you can use T1 as the default:

function f<T1>(): T1
function f<T1, T2 extends T1 = T1>(t2: T2): T1
function f<T1, T2>(t2?: T2): T1 | void {
}

f<string, string>('123') // ok
f<string>()              // ok
f<string>('123')         // OK

f<number>(123)         // OK

I would like to raise a different question though. Why have T2 at all id you don't want to specify it and just default it to T1. Why not just use T1. Type parameters that only appear in one position (either return type or a parameter type) are usually suspect. The whole point of type parameters is to establish relations between parameters or between parameters and return type.

Without knowing your full use case, this function makes more sense to me:


function f<T1>(): T1
function f<T1>(t2: T1): T1
function f<T1>(t2?: T1): T1 | void {
}

f<string>()              // ok
f('123')         // OK

f(123)         // OK

9
On

You can add a fallback for T2 to unknown or any:

function f<T1, T2 = unknown>(t2: T2): T1
function f<T1>(): T1

function f<T1, T2>(t2?: T2): T1 | void {
}

Or simply have one declaration for all the cases:

function f<T1, T2 = unknown>(t2?: T2): T1 | void {
}
// or
function f<T1, T2 = any>(t2?: T2): T1 | void {
}