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
func
accepts allAnimal
inputs but will return different types depending on whether its input is aDog
or not. That means I would declarefunc
to of the following overloaded type:Then, the function
test
just passes itsanimal
input to itsfunc
input and returns whatever type it gets back. To get that to happen, I would declaretest
like 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
Promise
s. It looks like you wantfunc
to either take aDog
and return aPromise<T>
, or take anAnimal
and return aPromise<Foo<T>>
, but not necessarily both. That is, a particularfunc
might only want aDog
and 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 callingtest3
but 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
A
is about as specific as I think I can get. It accepts any type of animalA
foranimal
, and a functionfunc
that will definitely acceptanimal
and 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.