英文:
How can I derive types from function argument?
问题
以下是您要翻译的内容:
"I want a function createApi
that will infer types based on the config passed in.
Here is what I have so far:
export type EndpointConfig<Input, Resp> = (http: Http) => RequestConfig<Input, Resp>;
export type RequestConfig<Input, Resp> = {
query: (input: Input) => Promise<Resp>
}
export type ApiConfig<M extends EndpointMap> = {
endpoints: { [K in keyof M]: EndpointConfig<M[K]['input'], M[K]['resp']> }
}
type EndpointMap = { [key: string]: { resp: any, input: any } };
export const createApi = <M extends EndpointMap>(config: ApiConfig<M>): ApiConfig<M> => {
// omit implementation
return config;
};
export interface Http {
get: <T>(url: string) => Promise<T>
}
// ============ using createApi ============
interface User {
username: string
}
export const api = createApi({
endpoints: {
users: http => ({
// here is where the types should come from
query: (input: string) => http.get<User>(`/users/${input}`),
}),
},
});
const { users } = api.endpoints;
// typeof users is EndpointConfig<any, any>
// but I want EndpointConfig<string, User>
希望这对您有所帮助。
英文:
I want a function createApi
that will infer types based on the config passed in.
Here is what I have so far:
export type EndpointConfig<Input, Resp> = (http: Http) => RequestConfig<Input, Resp>;
export type RequestConfig<Input, Resp> = {
query: (input: Input) => Promise<Resp>
}
export type ApiConfig<M extends EndpointMap> = {
endpoints: { [K in keyof M]: EndpointConfig<M[K]['input'], M[K]['resp']> }
}
type EndpointMap = { [key: string]: { resp: any, input: any } };
export const createApi = <M extends EndpointMap>(config: ApiConfig<M>): ApiConfig<M> => {
// omit implementation
return config;
};
export interface Http {
get: <T>(url: string) => Promise<T>
}
// ============ using createApi ============
interface User {
username: string
}
export const api = createApi({
endpoints: {
users: http => ({
// here is where the types should come from
query: (input: string) => http.get<User>(`/users/${input}`),
}),
},
});
const { users } = api.endpoints;
// typeof users is EndpointConfig<any, any>
// but I want EndpointConfig<string, User>
答案1
得分: 1
给定一个类型值
type ApiConfig<M extends EndpointMap> = {
endpoints: { [K in keyof M]: EndpointConfig<M[K]['input'], M[K]['resp']> }
}
对于某个 M
,编译器实际上无法从中正确推断出 M
。要做到这一点,需要能够从任意的索引访问类型(比如 T['input']
或 T['resp']
)中合成类型,但 TypeScript 目前还不能做到这一点。有一个相关的功能请求,可以在 microsoft/TypeScript#51612 中找到,但目前还不包括在语言中。
在这个功能得到实现之前,你需要绕过它。更容易的做法是推断一个类型参数,使其正好等于传入函数参数的类型,然后从中计算出所需的类型。所以,不要使用 config: ApiConfig<M>
,而是使用 config: {endpoints: E}
,然后返回 E
或从 E
计算出的某种类型。例如:
export const createApi = <
E extends Record<keyof E, EndpointConfig<any, any>>
>(config: { endpoints: E }): {
endpoints: { [K in keyof E]:
E[K] extends EndpointConfig<infer I, infer R> ?
EndpointConfig<I, R> : E[K]
}
} => {
// 忽略实现
return config;
};
现在当我们测试它时:
export const api = createApi({
endpoints: {
users: http => ({
query: (input: string) => http.get<User>(`/users/${input}`),
}),
},
});
/* const api: {
endpoints: {
users: EndpointConfig<string, User>;
};
} */
const { users } = api.endpoints;
// const users: EndpointConfig<string, User>
你会得到你所期望的行为。
英文:
Given a value of type
type ApiConfig<M extends EndpointMap> = {
endpoints: { [K in keyof M]: EndpointConfig<M[K]['input'], M[K]['resp']> }
}
for some M
, the compiler really can't properly infer M
from it. To do so would require that types could be synthesized from arbitrary indexed access types like T['input']
or T['resp']
, but TypeScript doesn't currently do that. There's an open feature request for it at microsoft/TypeScript#51612, but for now it's not part of the language.
Until an unless that gets implemented you'll need to work around it. It's a lot easier to infer a type parameter to be exactly the type of the passed in function argument, and then evaluate your desired type from that. So instead of config: ApiConfig<M>
, you'd have config: {endpoints: E}
and then return either E
or some type that is computed from E
. For example:
export const createApi = <
E extends Record<keyof E, EndpointConfig<any, any>>
>(config: { endpoints: E }): {
endpoints: { [K in keyof E]:
E[K] extends EndpointConfig<infer I, infer R> ?
EndpointConfig<I, R> : E[K]
}
} => {
// omit implementation
return config;
};
And now when we test it:
export const api = createApi({
endpoints: {
users: http => ({
query: (input: string) => http.get<User>(`/users/${input}`),
}),
},
});
/* const api: {
endpoints: {
users: EndpointConfig<string, User>;
};
} */
const { users } = api.endpoints;
// const users: EndpointConfig<string, User>
You get the behavior you're looking for.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论