这是 this question 的扩展
鉴于此代码:
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>
已返回。
更新 1
我无法让@jcalz版本工作,但我花了一段时间才意识到它与 promise 有关,但我不确定如何解决这个问题。以下是我的“丑陋但有效”方法和@jalz 的“它很好但很糟糕”方法:
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); });
最佳答案
我明白了
the function conditionally calls the given
func
with the givenanimal
. If given a dog, typeT
is returned. If given some other animal, a typeFoo<T>
is returned.
表示参数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);
}
希望有帮助。
更新 1
@daryl said :
This works for the definition, but not call sites. If I pass in a dog as the first parameter, my function must accept a dog and return an T, else it must accept an animal and return a Foo, At the call sites, it complains the the function isn't returning the other type (T or 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);
如果您尝试传递不同类型的函数,请详细说明用例。谢谢。
更新2
@daryl said :
This works for the definition, but not call sites. If I pass in a dog as the first parameter, my function must accept a dog and return an T, else it must accept an animal and return a Foo, At the call sites, it complains the the function isn't returning the other type (T or Foo)
好吧,我认为这与 Promise
没有太大关系。 s。看起来你想要 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>
。
我希望这有帮助。
关于typescript - 如何使用不同的回调重载参数干净地编写 TypeScript 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45087264/