gpt4 book ai didi

typescript - 如何干净地编写具有不同回调重载参数的 TypeScript 函数

转载 作者:搜寻专家 更新时间:2023-10-30 21:50:45 26 4
gpt4 key购买 nike

这是 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 given animal. If given a dog, type T is returned. If given some other animal, a type Foo<T> is returned.

表示参数func接受所有 Animal输入,但会根据其输入是否为 Dog 返回不同的类型或不。这意味着我会声明 func以下重载类型的 to:

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 没有太大关系。秒。看起来你想要 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com