使用 TypeScript 重载函数返回类型

huangapple go评论104阅读模式
英文:

Overload function return type with typescript

问题

我正在尝试构建一个通用的 "req" 函数,该函数以 URL 路径(字符串)和对象(GET/POST 参数)作为参数,并返回具有适当类型推断的结果。

这将是我的函数从中获取类型定义的接口:

interface ValidQueries{
    books: (q: {
        limit?: number;
        category?: string;
    }) => Book[];

    authors: (q: {
        name?: string;
    }) => Author[];
}

并尝试编写该函数:

const req = <K extends keyof ValidQueries>(path: string, params: Parameters<ValidQueries[K]>[0]) => {
    return useQuery([path, params], ({ queryKey }) => {
        const [, params] = queryKey;
        return httpRequest(path, params) as Promise<ReturnType<ValidQueries[K]>>;
    });
};

示例:

req("books", { limit: 10 }); // 返回类型应为 Book[],第二个参数应为与接口中匹配函数的 "q" 类型。

params 类型定义似乎不正确。我希望从接口中的函数(q)的第一个参数的类型中获取类型。

而返回类型 Promise<ValidQueries[K]> 似乎可以工作,但不太理想,因为它显示了所有可能的类型,如 Book[] | Author[],而不仅仅是如果我将 "books" 作为第一个参数传递,则只是 Book[],如果我将 "authors" 作为第一个参数传递,则只是 Author[]

英文:

I am trying to build a generic "req" function that takes an URL path (string), and object (GET/POST params) as arguments, and returns the results with the appropriate type inferred.

This would be the interface from where my functions gets its type defs:

interface ValidQueries{
    books: (q: {
        limit?: number;
        category?: string;
    }) =&gt; Book[];

    authors: (q: {
        name?: string;
    }) =&gt; Author[];
}

And an attempt to write the function:

const req = &lt;K extends keyof ValidQueries&gt;(path: string, params: (Parameters&lt;ValidQueries[K]&gt;[0])) =&gt; {
    return useQuery([path, params], ({ queryKey }) =&gt; {
        const [, params] = queryKey;
        return httpRequest(path, params) as Promise&lt;ReturnType&lt;ValidQueries[K]&gt;&gt;;
    });
};

Example:

req(&quot;books&quot;, { limit: 10 }); -> return type should appear as Book[], and 2nd arg should appear as type of "q" from the matching function in the interface.

the params type definition does not appear to be correct. I wanted to take the type of the first param from the functions in my interface (q).

And the return type Promise&lt;ValidQueries[K]&gt; sort of works, but its not ideal because I see all possible types, like Book[] | Author[], instead of just Book[] if I pass "books" as first arg, or just Author[] if I pass "authors" as the first arg...

答案1

得分: 1

以下是翻译好的部分:

Most importantly, when you use generics you need to have some anchor, that TypeScript may infer from.
最重要的是,当你使用泛型时,你需要有一些 TypeScript 可以从中推断的锚点。

In this function, you use path: string (so any string), and the only argument using generic K is params.
在这个函数中,你使用了 path: string(任何字符串),而唯一使用泛型 K 的参数是 params

Because of that, when you call req("books", { limit: 10 }), to compute the generic K it will do something like Extract<ValidQueries[keyof ValidQueries], { limit: 10 }&gt;.
因此,当你调用 req("books", { limit: 10 }) 时,用于计算泛型 K 的过程会类似于 Extract<ValidQueries[keyof ValidQueries], { limit: 10 }&gt;

To fix that, you may use K for the path argument - path: K. That will help to find the proper method from ValidQueries.
要解决这个问题,你可以在路径参数中使用 K - path: K。这将有助于从 ValidQueries 中找到适当的方法。

It should also solve the problem with Book[] | Author[], unless the useQuery has incorrect types (then you may push your as … clause to the end, or type the function return type directly).
这也应该解决了 Book[] | Author[] 的问题,除非 useQuery 的类型不正确(那么你可以将 as … 子句放到末尾,或直接为函数指定返回类型)。

Additionally, you may use rest parameters like ...params: Parameters<…>, so it will allow passing any number of ValidQueries[K] arguments down. It will also add the q: name to the parameter in the IDE, that you expected.
此外,你可以使用类似 ...params: Parameters<…> 的剩余参数,这样它将允许传递任意数量的 ValidQueries[K] 参数。它还会在 IDE 中添加你预期的参数名称 q:

Summing up, you have something like this:
总结一下,你有类似如下的内容:

interface ValidQueries {
  books: (q: { limit?: number, category?: string }) => Book[];
  authors: (q: { name?: string }) => Author[];
}

const req = <K extends keyof ValidQueries, T extends ValidQueries[K]>(path: K, ...params: Parameters<T>) => {
  return useQuery([ path, params ], ({ queryKey }) => {
    const [ , params ] = queryKey;
    return httpRequest(path, params) as Promise<ReturnType<T>>;
  });
};

It has a small adjustment though, that the T type is created here automatically, as an alias. You could use the full Parameters<ValidQueries[K]> form (and similarly for the return type).
不过有一个小的调整,即 T 类型在这里被自动创建为别名。你可以使用完整的 Parameters<ValidQueries[K]> 形式(类似地,也可以用于返回类型)。

英文:

Most importantly, when you use generics you need to have some anchor, that TypeScript may infer from. In this function, you use path: string (so any string), and the only argument using generic K is params.

Because of that, when you call req(&quot;books&quot;, { limit: 10 }), to compute the generic K it will do something like Extract&lt;ValidQueries[keyof ValidQueries], { limit: 10 }&gt;. To fix that, you may use K for the path argument - path: K. That will help to find the proper method from ValidQueries.

It should also solve the problem with Book[] | Author[], unless the useQuery has incorrect types (then you may push your as … clause to the end, or type the function return type directly).

Additionally, you may use rest parameters like ...params: Parameters&lt;…&gt;, so it will allow passing any number of ValidQueries[K] arguments down. It will also add the q: name to the parameter in the IDE, that you expected.

Summing up, you have something like this:

interface ValidQueries {
  books: (q: { limit?: number, category?: string }) =&gt; Book[];
  authors: (q: { name?: string }) =&gt; Author[];
}

const req = &lt;K extends keyof ValidQueries, T extends ValidQueries[K]&gt;(path: K, ...params: Parameters&lt;T&gt;) =&gt; {
  return useQuery([ path, params ], ({ queryKey }) =&gt; {
    const [ , params ] = queryKey;
    return httpRequest(path, params) as Promise&lt;ReturnType&lt;T&gt;&gt;;
  });
};

It has a small adjustment though, that the T type is created here automatically, as an alias. You could use the full Parameters&lt;ValidQueries[K]&gt; form (and similarly for the return type).

huangapple
  • 本文由 发表于 2023年2月14日 02:05:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75439652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定