英文:
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;
}) => Book[];
authors: (q: {
name?: string;
}) => Author[];
}
And an attempt to write the function:
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]>>;
});
};
Example:
req("books", { 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<ValidQueries[K]>
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 }>
.
因此,当你调用 req("books", { limit: 10 })
时,用于计算泛型 K
的过程会类似于 Extract<ValidQueries[keyof ValidQueries], { limit: 10 }>
。
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("books", { limit: 10 })
, to compute the generic K
it will do something like Extract<ValidQueries[keyof ValidQueries], { limit: 10 }>
. 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<…>
, 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 }) => 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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论