我正在开发一个 Astro + TypeScript 项目,其中几乎没有具有特定模式的数据集合。
我使用 Zod 库来定义和构建每个数据集合,其简化方式如下所示:
import { defineCollection, z } from 'astro:content';
export const authorsCollection = defineCollection({
schema: z.object({
name: z.string(),
}),
});
export const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
status: z.enum(['publish', 'trash', 'draft']).default('draft'),
}),
});
export const collections = {
authors: authorsCollection,
blog: blogCollection,
};
我正在尝试编写一个辅助函数来根据集合的 ID 以不同方式处理集合(例如,能够根据博客文章中的状态字段过滤条目,而作者集合中没有该条目)。
这是我正在使用的功能:
import { getCollection } from 'astro:content';
import { collections as c } from '@content/config';
const collections = Object.keys (c);
type CollectionType = keyof (typeof c); // This solely translates to "authors" | "blog" string literal type.
type CollectionSettings = {
status?: string[]; // Just for illustrative purposes.
};
export async function retrieveCollection (id: CollectionType, preferences = <CollectionSettings>{}) {
const settings = Object.assign ({
status: ['publish'], // Set default values.
}, preferences);
let entries = await getCollection (id); // Retrieve an initial list of content entries by its id.
if (id === 'blog') { // This is where I distinguish whether or not the current request to the function belongs to a certain collection but the compiler doesn't seem to recognize it.
entries = entries.filter (({ data: { status }}) => settings.status.includes (status)); // Here lies the error and the main reason for my current question.
}
// Perform another procedures such as filtering, searching fields, sorting entries by date, etc.
return entries;
};
功能上我没有任何抱怨,因为一切都按其应有的方式进行;然而,编译器一直抱怨
Property 'status' does not exist on 'authorsCollection' type
,我希望能够修复它而不诉诸 @ts-ignore
指令。
问题似乎是 TypeScript 无法识别条件块中集合的特定类型。例如,在
if (id === 'blog')
块中,TypeScript 无法正确推断条目符合 BlogPost 架构,导致访问 status 属性(显然不存在)时出错。
我之前在这个网站上读过与
TypeScript type narrowing
相关的问题,用ChatGPT和GitHub Copilot检查过,没有结果。
所以我的问题是:
如果有任何有关如何改进我的类型结构和函数逻辑以确保 TypeScript 正确处理每个集合的特定模式的建议,我将不胜感激。
提前谢谢大家。
最后我感到绝望,因为找不到令人满意的解决方案,我决定简单地强制强制转换为
CollectionEntry
,如下所示:
import type { CollectionEntry } from 'astro:content';
export async function retrieveCollection (id: CollectionType, preferences = <CollectionSettings>{}) {
type EntryType = CollectionEntry <typeof id>;
// The rest of the code remained the same as the code example above.
if (id === 'blog') {
entries = entries.filter (({ data }: EntryType) => { }); // Type casting here.
}
}
虽然这似乎让编译器满意,但我仍然愿意学习更好的解决方案。