Typescript函数重载,通用可选参数

时间:2020-06-03 13:44:56

标签: typescript overloading

我无法理解如何正确地将作为参数传递的函数键入到另一个函数,其中传递的函数可以具有2个不同的签名,一个带有参数,另一个不带参数。

我的案件简化了:


type ApiMethod<T, U> = {
  (payload?: T): Promise<U>;
};

function callFactory<T, U>(apiMethod: ApiMethod<T, U>) {
  return async (payload?: T): Promise<U> => {
    if (payload) {
      return await apiMethod(payload);
    } else {
      return await apiMethod();
    }
  };
}

const apiMethodExample1: (payload: string) => Promise<string> = (payload) => {
  return Promise.resolve('some payload: ' + payload);
};

const apiMethodExample2: () => Promise<string> = () => {
  return Promise.resolve('no payload');
};

const call1 = callFactory(apiMethodExample1); // here TS complains
const call2 = callFactory(apiMethodExample2);

const value1 = call1('examplePayload').then((value: string) => console.log(value));
const value2 = call2().then((value) => console.log(value));

here the code in the TS playground

我的问题是TS抱怨

const call1 = callFactory(apiMethodExample1);

Argument of type '(payload: string) => Promise<string>' is not assignable to parameter of type 'ApiMethod<string, string>'.
  Types of parameters 'payload' and 'payload' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.

我觉得我没有正确地重载 apiMethod 参数 但我所有其他尝试也都失败了

我已经看过以下答案:Typescript function overloads with generic optional parameters

但无法将其应用于我的案子

感谢您的帮助

2 个答案:

答案 0 :(得分:3)

给出这种类型:

T

如果您给我一个type ApiMethod<T, U> = { (payload: T): Promise<U>; (payload?: T): Promise<U>; }; 类型的值f,我应该可以呼叫ApiMethod<string, string>,并且我应该 也可以呼叫{{ 1}}。重载的函数具有多个调用签名,并且需要每个调用签名。

如果我打电话给f("someString"),而一切都爆炸了,那么您还没有给我有效的f()。这就是编译器为f()抱怨的地方。


让我稍微修改ApiMethod<string, string>的实现:

apiMethodExample1

我在这里所做的只是大写apiMethodExample1,它是一个const apiMethodExample1: (payload: string) => Promise<string> = (payload) => { return Promise.resolve('some payload: ' + payload.toUpperCase()); }; ,因此它应该有一个payload方法。从类型系统的角度来看,这与您的string版本没有什么不同,因为从函数外部看不到实现细节。

如果编译器没有对此抱怨:

toUpperCase()

然后因为apiMethodExample1的类型被推断为

const call1 = callFactory(apiMethodExample1); 

所以您可以这样做:

call1

在运行时会爆炸

// const call1: (payload?: string | undefined) => Promise<string>

问题在于call1().then(s => console.log(s)); 只能安全地用作// TypeError: payload is undefined ,而不能用作apiMethodExample1所需的全套呼叫签名。


请注意,(payload: string): Promise<string>很好,因为单个签名ApiMethod<string, string>可分配给两个呼叫签名。 apiMethodExample2可分配给() => Promise<string>可能令人惊讶,但这是因为您可以安全地将前者用作后者,因为前者将忽略传递给它的任何参数。有关更多信息,请参见名为Why are functions with fewer parameters assignable to functions that take more parameters?的TypeScript常见问题解答条目。


此外:请注意,如果您的代码不仅仅是简化的示例,我强烈建议删除第一个重载签名,因为任何满足第二个重载签名的函数也将满足第一个重载签名。因此,这个特定示例实际上与过载本身并没有关系。如果您只是写,就会得到相同的行为

() => Promise<string>

好的,希望能有所帮助;祝你好运!

Playground link to code


更新:

您似乎想输入(payload: string) => Promise<string>来接受两种类型,而不是根本就不在乎type ApiMethod<T, U> = { (payload?: T): Promise<U>; }; 。如果是这样,我会这样写:

callFactory()

内部没有条件代码;它只是将参数传播到调用中。然后键入ApiMethod<T, U>,以便它返回的函数采用与传入的function callFactory<T extends [] | [any], U>(apiMethod: (...payload: T) => Promise<U>) { return async (...payload: T): Promise<U> => { return await apiMethod(...payload); }; } 相同的参数。尚不清楚为什么您甚至需要callFactory,因为它所做的只是返回与参数相同类型的东西(apiMethodcallFactory基本上是同一件事),但这只是您的简化示例代码,我猜。

无论如何,此后的一切都会起作用:

callFactory(apiMethodExample1)

希望有帮助。再次祝你好运。

Playground link to code

答案 1 :(得分:0)

在使用此模式之前,我已经向类中的方法添加了“重载”。

static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            int t = 4;
            int f = ++t;
            if(t<3)
            {
                if (f < 3)
                    Console.WriteLine("No");
            }
            else if(t>3)
            {
                Console.WriteLine("Yes");
                if(f>3)
                {
                    Console.WriteLine("DOUBLE YES");
                    break;
                }

            }
            Console.WriteLine(f);
            Console.WriteLine(t);
        }
相关问题