我试图找到一种相对通用的方法来键入帖子正文和回复,以及它们的API路径(在nextjs应用程序中).
为此,我希望编译器强制我向所有API路由添加body
类型和return
类型,这是我通过以下接口实现的:
export interface PostTypeMapping extends Record<string, {body: unknown, return: unknown}> {
"/api/taskCompletions": {body: PostCompletionBody, return: void},
"/api/task": {body: PostTaskBody, return: void},
}
到现在为止,一直都还不错.我可以在API路由中使用这种类型,如下所示:
async (req, res: NextApiResponse<PostTypeMapping["api/task"]["return"]>) => {
//...
}
但是,当我试图编写一个包装器,从URL自动推断帖子正文和返回类型时,我在第await fetch(url,
行中得到了一个错误:
"PostTypeMapping"类型的键不能分配给"RequestInfo"类型的参数.
export async function fetchPost<T extends keyof PostTypeMapping>(url: T, body: PostTypeMapping[T]["body"]): Promise<PostTypeMapping[T]["return"]> {
try {
const res = await fetch(url, { // <- The error above occurs here
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
});
if(res.status === 201 || res.status === 204){
return;
}
return res.json();
} catch (err: any){
return {message: "Fehler: " + err.message};
}
}
Why can url
, which is typed as keyof PostTypeMapping
, be a number?
我进行了进一步的研究,在大多数情况下,extends Record<string, {body: unknown, return: unknown}>
似乎做了我想做的事情(迫使我向接口的所有条目添加一个body和返回类型),但允许数字键和字符串.为什么?这两种情况都是不允许的.
export interface PostTypeMapping extends Record<string, {body: unknown, return: unknown}> {
"/api/taskCompletions": {body: PostCompletionBody, return: void},
"/api/task": {body: PostTaskBody, return: void},
1: {body: void, return: void}, // why is this legal? 1 is not a string
2: "asd" // not allowed -> Property '2' of type '"asd"' is not assignable to 'string' index type '{ body: unknown; return: unknown; }'
"asd": "asd" // not allowed -> Property '"asd"' of type '"asd"' is not assignable to 'string' index type '{ body: unknown; return: unknown; }
}
EDIT:
多亏了T.J.Crowder,这个问题的简化重现可以在https://tsplay.dev/Nr5X2w处找到