鉴于此代码:
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);
}
是否有更简洁的方法来编写不需要A类型参数的重载?或者也许是写作任何一个的清洁工?基本上,该函数有条件地使用给定的func
调用给定的animal
。如果给狗,则返回类型T
。如果给出其他动物,则返回类型Foo<T>
。
我无法让@jcalz版本工作,但我花了一段时间才意识到这与承诺有关,但我不知道如何解决这个问题。下面是我的&#34;丑陋但它有效&#34;方法论,@ jalz&#34;它很好但是它已经破坏了#34;方法:
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); });
答案 0 :(得分:1)
我理解
该函数有条件地使用给定的
func
调用给定的animal
。如果给狗,则返回类型T
。如果给出其他动物,则返回类型Foo<T>
。
表示参数func
接受所有Animal
输入,但会返回不同类型,具体取决于其输入是否为Dog
。这意味着我会将func
声明为以下重载类型:
type AnimalFunc<T> = {
(dog: Dog): T;
(animal: Animal): Foo<T>;
}
然后,函数test
只将其animal
输入传递给它的func
输入,并返回它返回的任何类型。要实现这一点,我会像这样声明test
:
function test<T>(dog: Dog, func: AnimalFunc<T>): T;
function test<T>(animal: Animal, func: AnimalFunc<T>): Foo<T>;
function test<T>(animal: Animal, func: AnimalFunc<T>): T | Foo<T> {
return func(animal);
}
希望有所帮助。
@daryl said:
这适用于定义,但不适用于呼叫站点。如果我传入一只狗作为第一个参数,我的函数必须接受一个狗并返回一个T,否则它必须接受一个动物并返回一个Foo,在调用站点,它会抱怨该函数没有返回另一个类型(T或Foo)
在不知道所有用例的情况下,我无法确定最佳定义是什么。如果你真的有AnimalFunc<T>
类型的函数,它应该可以工作:
function func1(dog: Dog): string;
function func1(animal: Animal): Foo<string>;
function func1(animal: Animal): string | Foo<string> {
if (animal instanceof Dog) {
return "woof";
}
return new Foo<string>();
};
var dog: Dog = new Dog();
var cat: Animal = new Animal();
var dogTest: string = test(dog, func1);
var catTest: Foo<string> = test(cat, func1);
如果您尝试传递其他类型的功能,请说明用例。感谢。
@daryl said:
这适用于定义,但不适用于呼叫站点。如果我传入一只狗作为第一个参数,我的函数必须接受一个狗并返回一个T,否则它必须接受一个动物并返回一个Foo,在调用站点,它会抱怨该函数没有返回另一个类型(T或Foo)
好的,我不认为这与Promise
有很大关系。看起来您希望func
获取Dog
并返回Promise<T>
,或获取Animal
并返回Promise<Foo<T>>
,但不一定都是。也就是说,特定func
可能只需要Dog
,并且不会接受Cat
。这不是我最初的理解。
对于这种情况,那么我想说你想做:
function test3<T>(dog: Dog, func: (dog: Dog) => Promise<T>): Promise<T>;
function test3<T>(animal: Animal, func: (animal: Animal) => Promise<Foo<T>>): Promise<Foo<T>>;
function test3<T, A extends Animal>(animal: A, func: (animal: A) => Promise<T> | Promise<Foo<T>>): Promise<T> | Promise<Foo<T>> {
return func(animal);
}
请注意,test3
(前两行)的声明是为了调用者的利益而输入的,而实现(第三行)的输入是为了实施者。如果你所关心的只是为那些呼叫test3
的人提供类型安全但在你的实现中足够安全,你不需要TS为你验证类型,那么你可以把它实现为:
function test3<T>(dog: Dog, func: (dog: Dog) => Promise<T>): Promise<T>;
function test3<T>(animal: Animal, func: (animal: Animal) => Promise<Foo<T>>): Promise<Foo<T>>;
function test3(animal: any, func: any): any {
return func(animal); // fine, but even return animal(func) would be accepted here, to disastrous results at runtime
}
具有通用A
的实现签名与我认为可以获得的具体相同。它接受A
的任何类型的动物animal
,以及肯定会接受func
并返回animal
或Promise<T>
的函数Promise<Foo<T>>
}。这对于调用func(animal)
来说足够安全,但你仍然可以通过实现类似
function test3<T, A extends Animal>(animal: A, func: (animal: A) => Promise<T> | Promise<Foo<T>>): Promise<T> | Promise<Foo<T>> {
return Promise.resolve(new Foo<T>()); // no error!!
}
会导致第一个重载声明出现问题,因为它不会返回Promise<T>
。
我希望这有帮助。