Tìm kiếm trang web

zod - Cách tạo một schema riêng cho các bài viết "nháp" và "đã xuất bản" và cách làm cho TypeScript dễ dàng phân biệt chúng (Bộ sưu tập Nội dung Astro v2)


Tôi đang cố gắng thiết lập Content Collections cho blog của mình trong Astro. Tôi hầu như ngay lập tức gặp phải vấn đề với các bài viết nháp: chúng thiếu một số trường schema, và Zod không thích điều đó.

Đào sâu vào tài liệu của Zod, tôi đã nghĩ ra z.discriminatedUnion, vì vậy schema cho "drafts" sẽ lỏng lẻo hơn nhiều so với các bài đăng đã xuất bản:

// src/content/config.ts
// (simplified example)

const publishedPostSchema = z.object({
  title: z.string(),
  author: z.enum(["John Doe", "Jane Doe"]),
  dateCreated: z.string().transform((str) => new Date(str)),
  description: z.string(),
  draft: z.literal(null),
});

const draftPostSchema = z.object({
  title: z.string().nullable(),
  author: z.enum(["John Doe", "Jane Doe"]).nullable(),
  dateCreated: z
    .string()
    .transform((str) => new Date(str))
    .nullable(),
  draft: z.literal(true),
});

const exampleBlogCollection = defineCollection({
  schema: z.discriminatedUnion("draft", [publishedPostSchema, draftPostSchema]),
});

export const collections = {
  blog: exampleBlogCollection,
};

Điều này hoạt động, nhưng tôi vẫn còn hai vấn đề nhỏ:

1. Điều này buộc tôi phải có một trường null "nháp" trên tất cả các bài viết đã xuất bản. Tôi muốn cho phép các giá trị null, false và undefined, nhưng z.discriminatedUnion chỉ chấp nhận các literal trên trường phân biệt. Có cách nào giải quyết vấn đề này không?

2. Kiểu kết quả là một hợp nhất phân biệt, nhưng nó vẫn cần rất nhiều công việc để đảm bảo công cụ TypeScript biết rằng những gì tôi đang truyền xuống không phải là giá trị có thể null:

Điều này không hoạt động (lỗi TypeScript):

const allPosts = await getCollection('blog')

const publishedPosts = rawPosts.filter(post => post.data.draft === null)

publishedPosts .sort(
  (a, b) =>
    +(b.data.dateCreated) - +(a.data.dateCreated) // TS error: `dateCreated` can be `null`…
);

Điều này hoạt động, nhưng… nó hơi dài dòng:

const allPosts = await getCollection('blog')

type PublishedPost = CollectionEntry<"blog"> & { data: { draft: null }}


function isPublishedPost(post: typeof allPosts[number]): post is PublishedPost {
    return post.data.draft === null
}

const publishedPosts = allPosts.filter(isPublishedPost)

publishedPosts .sort(
  (a, b) =>
    +(b.data.dateCreated) - +(a.data.dateCreated) // O.K.
);

Đây có phải là cách 'mong đợi' để xử lý các schema, hay tôi đã bỏ lỡ điều gì quan trọng?