【问题标题】:Typescript does not recognize an expected overloaded function signatureTypescript 无法识别预期的重载函数签名
【发布时间】:2021-01-23 15:50:15
【问题描述】:

我正在努力解决打字稿重载问题。

我正在使用带有 typescript 的 googleapis 库并尝试获取所有 tagmanager 帐户记录。 由于如果响应正文包含 nextPageToken,googleapis 列表函数需要分页,所以我想创建一个对所有内容进行分页并获取所有记录的函数。

我的想法是创建一个这样的咖喱函数。它以 list 函数作为参数,并不断调用 list 函数与 nextPageToken 直到 nextPageToken 不包含在 list 函数返回的数据中。

// ex. 1)
const allAccounts = await paginateAll(tagmanager.accounts.list)({
  // specify tagmanager.accounts.list parameter
});

// ex. 2)
const allContainers = await paginateAll(tagmanager.accounts.containers.list)({
  // specify tagmanager.accounts.containers.list parameter
});

我创建了一个如下所示的paginateAll 签名,但打字稿似乎无法解决适当的重载问题。

export const paginateAll = <P1, P2, R>(
  list: (params?: P1, options?: P2) => Promise<R>,
): ((arg1: P1, arg2: P2) => Promise<Array<R>>) => async (a, b) => {
    // some procedure...
  };

const fetchAllAccounts = paginateAll(tagmanager.accounts.list)
                                     ^^^

=== ERROR ===
Argument of type '{ (params: Params$Resource$Accounts$List, options: StreamMethodOptions): GaxiosPromise<Readable>; (params?: Params$Resource$Accounts$List | undefined, options?: MethodOptions | undefined): GaxiosPromise<...>; (params: Params$Resource$Accounts$List, options: StreamMethodOptions | BodyResponseCallback<...>, callback: ...' is not assignable to parameter of type '(params?: BodyResponseCallback<Schema$ListAccountsResponse> | undefined, options?: unknown) => Promise<unknown>'.
  Types of parameters 'params' and 'params' are incompatible.
    Type 'BodyResponseCallback<Schema$ListAccountsResponse> | undefined' is not assignable to type 'Params$Resource$Accounts$List'.
      Type 'undefined' is not assignable to type 'Params$Resource$Accounts$List'.

googleapis list 函数有 6 个重载,如下所示。我希望paginateAll 选择第二个签名。

1. list(params: Params$Resource$Accounts$List, options: StreamMethodOptions): GaxiosPromise<Readable>;
2. list(params?: Params$Resource$Accounts$List, options?: MethodOptions): GaxiosPromise<Schema$ListAccountsResponse>;
3. list(params: Params$Resource$Accounts$List, options: StreamMethodOptions | BodyResponseCallback<Readable>, callback: BodyResponseCallback<Readable>): void;
4. list(params: Params$Resource$Accounts$List, options: MethodOptions | BodyResponseCallback<Schema$ListAccountsResponse>, callback: BodyResponseCallback<Schema$ListAccountsResponse>): void;
5. list(params: Params$Resource$Accounts$List, callback: BodyResponseCallback<Schema$ListAccountsResponse>): void;
6. list(callback: BodyResponseCallback<Schema$ListAccountsResponse>): void;

如果你们能告诉我为什么会这样,我将不胜感激......

==== 更新 ====

我用 TypeScript Playground 重现了我的问题中的错误。 (我让它不咖喱让它更简单)

type Params = {
    value1: string;
    value2: string;
}

type Options1 = {
    option1Value: string;
};

type Options2 = {
    option2Value: string;
};

type Resonse1 = {
    response1Value: string;
}

type Response2 = {
    response2Value: string;
}

type Callback<T> = () => T

declare function func(params: Params, options: Options1): Promise<Resonse1>;
declare function func(params?: Params, options?: Options2): Promise<Response2>;
declare function func(params: Params, options: Options1 | Callback<Resonse1>, callback: Callback<Resonse1>): void;
declare function func(params: Params, options: Options2 | Callback<Response2>, callback: Callback<Response2>): void;
declare function func(params: Params, callback: Callback<Response2>): void;
declare function func(callback: Callback<Response2>): void;

const anotherFunc = async <P1, P2, R>(
  fn: (params?: P1, options?: P2) => Promise<R>,
): Promise<R> => {
    return fn();
}

const test = anotherFunc(func);

【问题讨论】:

  • 发生这种情况是因为重载决议只有在你直接调用重载函数时才会发生。它不会通过通用推理发生,最终会选择第一个或最后一个重载。请参阅this comment 及其所属的 GitHub 问题。您能否将问题中的代码设为minimal reproducible example,可以将其按原样放入The TypeScript Plaground 等独立IDE 中以演示您的问题?这将允许我编写一个适合你的版本。
  • @jcalz 感谢您的评论!我在我的问题中粘贴了更多可重复的示例。 (我不知道它是否真的很小)如果您向我展示此问题的解决方法,我将非常感激...谢谢。

标签: typescript google-api typescript-typings generic-programming typescript-generics


【解决方案1】:

除非您实际使用输入直接调用重载函数,否则编译器无法选择重载。如果您将重载函数传递给另一个函数并尝试使用泛型类型推断,编译器将不会按照您想要的方式执行重载解析。它只是选择一个重载,通常是第一个或最后一个。这是 TypeScript 的设计限制,请参阅microsoft/TypeScript#30369 了解更多信息。

这里的解决方法是让 选择您要使用的签名。这是一种方法:

const f: (params?: Params, options?: Options2) => Promise<Response2> = func;
const test = anotherFunc(f);

在这里,我们将func 分配给变量f,该变量的类型对应于仅具有我们想要推断的调用签名的函数。此分配是允许的。由于f 没有过载,调用anotherFunc(f) 可以正常工作。

Playground link to code

【讨论】:

  • 在我的情况下似乎无法明确声明类型,所以我必须考虑其他方式。但无论如何,谢谢你的回答。你的回答将是我了解更多关于打字稿打字的第一步!
猜你喜欢
  • 1970-01-01
  • 2016-11-25
  • 2022-10-25
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多