The discussion in implement callable interface is useful but does not completely my answer my question.
interface lol {
(a: number): (b: number) => string
// (a: string): (b: string) => string // overloaded will not work
}
let l: lol = function(a: number) {
return (b: number) => {
return (a b).toString()
}
}
This snippet works but when the overloaded function is uncommented it fails. I've tried various methods and haven't figure out how to implement the overloaded version.
A slightly convoluted but real world implementation I'm trying to understand is fp-ts
style interfaces. Ignoring the complex types it's an overloaded callable interface is what I understand. How would such a thing have a concrete implementation.
export interface Traverse1<T extends URIS> {
<F extends URIS4>(F: Applicative4<F>): <A, S, R, E, B>(
ta: Kind<T, A>,
f: (a: A) => Kind4<F, S, R, E, B>
) => Kind4<F, S, R, E, Kind<T, B>>
<F extends URIS3>(F: Applicative3<F>): <A, R, E, B>(
ta: Kind<T, A>,
f: (a: A) => Kind3<F, R, E, B>
) => Kind3<F, R, E, Kind<T, B>>
<F extends URIS3, E>(F: Applicative3C<F, E>): <A, R, B>(
ta: Kind<T, A>,
f: (a: A) => Kind3<F, R, E, B>
) => Kind3<F, R, E, Kind<T, B>>
<F extends URIS2>(F: Applicative2<F>): <A, E, B>(
ta: Kind<T, A>,
f: (a: A) => Kind2<F, E, B>
) => Kind2<F, E, Kind<T, B>>
<F extends URIS2, E>(F: Applicative2C<F, E>): <A, B>(
ta: Kind<T, A>,
f: (a: A) => Kind2<F, E, B>
) => Kind2<F, E, Kind<T, B>>
<F extends URIS>(F: Applicative1<F>): <A, B>(ta: Kind<T, A>, f: (a: A) => Kind<F, B>) => Kind<F, Kind<T, B>>
<F>(F: Applicative<F>): <A, B>(ta: Kind<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, Kind<T, B>>
}
CodePudding user response:
The implementation declaration must satisfy all of the overloads stated by the interface so, in your case, it might end up looking something like this:
let l: lol = function <T extends number | string>(a: T): (b: T) => string {
throw Error("not implemented");
}
In my experience, you'll need to do a lot of type-checking and casting to get the implementation right.
CodePudding user response:
After @spender's helpful explanation I realized what the fp-ts style interface is actually doing. By having <F extends ...>
check it is possible to resolve the overloaded functions to a single concrete implementation as in the example below.
interface lol {
<T extends number>(a: T): (b: T) => string
<T extends string>(a: T): (b: T) => string
}
let l: lol = function(a: number) {
return (b: number) => {
return (a b).toString()
}
}
This instantiates the T
as number so the <T extends string>
overloading is never resolved (?? is that the right word for it).