I'm working on an Astro + TypeScript project where I have few data collections with specific schemas.
I'm using the Zod library to define and structure each data collection, which in a simplified way looks something like this:
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,
};
I'm trying to write a helper function to handle collections differently based on their ID (e.g. be able to filter entries based on their status field in the blog posts while not having it in the authors collection).
Here is the function I'm using:
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;
};
Functionally I have no complaints, as everything works as it should; however, the compiler keeps complaining about Property 'status' does not exist on 'authorsCollection' type
and I would like to be able to fix it without resorting to @ts-ignore
directive.
The problem seems to be that TypeScript does not recognize the specific type of the collection within the conditional block. For example, within the if (id === 'blog')
block, TypeScript does not correctly infer that entries conforms to the BlogPost schema, leading to an error when accessing the status property (which obviously does not exist).
I have previously read the questions related to TypeScript type narrowing
on this site, checked with ChatGPT and GitHub Copilot without results.
So my questions are:
- How can I improve the type discrimination so that TypeScript recognizes the specific collection schema based on the string literal type?
- Is there a better way to handle these types to avoid such type inference issues?
I would appreciate any suggestions on how to improve my type structure and function logic to ensure TypeScript correctly handles the specific schemas for each collection.
Thank you all in advance.