This is an extension of this question
Given this code:
class Animal {
a: string;
}
class Dog extends Animal {
b: string;
}
class Foo<T>{}
function test<T,A extends Dog>(animal:A, func: (p: A) => T): T;
function test<T,A extends Animal>(animal:A, func: (p: A) => Foo<T>): Foo<T>;
function test<T,A extends Animal>(animal:A, func: (p: A) => T|Foo<T>): T|Foo<T> {
return func(animal);
}
is there a cleaner way of writing the overload that doesn't require the A type Parameter? Or maybe a cleaner of writing any of it? Basically the function conditionally calls the given func with the given animal. If given a dog, type T is returned. If given some other animal, a type Foo<T> is returned.
Update 1
I was unable to get @jcalz version to work, but it took me a while to realize it was related to promises, but I'm not sure how to resolve the issue. Below is my "ugly but it works" methodology, and @jalz's "it's nice but it's broke" methodology:
class Animal {
a: string;
}
class Dog extends Animal {
b: string;
}
class Foo<T>{ }
function test<T, A extends Dog>(animal: A, func: (p: A) => Promise<T>): Promise<T>;
function test<T, A extends Animal>(animal: A, func: (p: A) => Promise<Foo<T>>): Promise<Foo<T>>;
function test<T, A extends Animal>(animal: A, func: (p: A) => Promise<T> | Promise<Foo<T>>): Promise<T | Foo<T>> {
return func(animal);
}
const foo: Promise<Foo<string>> = test(new Animal(), (a) => { return Promise.resolve(new Foo<string>()); });
const other: Promise<string> = test(new Dog(), (d) => { return Promise.resolve(d.b); });
type AnimalFunc<T> = {
(dog: Dog): Promise<T>;
(animal: Animal): Promise<Foo<T>>;
}
function test2<T>(dog: Dog, func: AnimalFunc<T>): Promise<T>;
function test2<T>(animal: Animal, func: AnimalFunc<T>): Promise<Foo<T>>;
function test2<T>(animal: Animal, func: AnimalFunc<T>): Promise<T | Foo<T>> {
return func(animal);
}
const foo2: Promise<Foo<string>> = test2(new Animal(),
(a) => {
return Promise.resolve(new Foo<string>());
}); // Errors: TS2345 Argument of type '(a: any) => Promise<Foo<string>>' is not assignable to parameter of type 'AnimalFunc<string>'.
// Type 'Promise<Foo<string>>' is not assignable to type 'Promise<string>'.
// Type 'Foo<string>' is not assignable to type 'string'.TypeScript Virtual Projects C: \_Dev\CRM\WebResources\webresources\new_\scripts\Payment.ts 498 Active
const other2: Promise<string> = test2(new Dog(), (d) => { return Promise.resolve(d.b); });
I understand
to mean that the parameter
funcaccepts allAnimalinputs but will return different types depending on whether its input is aDogor not. That means I would declarefuncto of the following overloaded type:Then, the function
testjust passes itsanimalinput to itsfuncinput and returns whatever type it gets back. To get that to happen, I would declaretestlike this:Hope that helps.
Update 1
@daryl said:
Without knowing all your use cases I can't tell what the best definition would be. If you really have a function of type
AnimalFunc<T>it should work:If you are trying to pass in a different type of function, please spell out the use cases. Thanks.
Update 2
@daryl said:
Okay, I don't think this has much to do with
Promises. It looks like you wantfuncto either take aDogand return aPromise<T>, or take anAnimaland return aPromise<Foo<T>>, but not necessarily both. That is, a particularfuncmight only want aDogand will not accept aCat. That's not how I understood it originally.For this case, then I'd say you want to do:
Note that the declarations of
test3(the top two lines) are typed for the benefit of the caller, while the implementation (the third line) is typed for the benefit of the implementer. If all you care about is type safety for people callingtest3but are secure enough in your implementation that you don't need TS to verify the types for you, then you can just implement it as:The implementation signature with the generic
Ais about as specific as I think I can get. It accepts any type of animalAforanimal, and a functionfuncthat will definitely acceptanimaland return either aPromise<T>or aPromise<Foo<T>>. This is safe enough for callingfunc(animal), but you could still fool the typechecker by having an implementation likewhich would cause problems with the first overload declaration since it doesn't ever return a
Promise<T>.I hope this has helped.