#How to define Typescript with getStaticPaths and CollectionEntry

3 messages · Page 1 of 1 (latest)

icy violet
#

Hey y'all I am a new developer trying to wrap my head around using typescript for the first time. Currently I am running into an issue of losing type definition when defining the render template of getStaticPaths.

So I have gotten typescript to work up until the following lines, where it believes both tag and posts are type never.

const { posts } = Astro.props;```

I would also appreciate a more simplified description of what those lines are doing as I truly do not understand what is happening.

Below is my code for this page following the astro tutorial guide:


import BaseLayout from "...";
import Formatter from "..."
import { getCollection } from "astro:content";
import type { CollectionEntry } from "astro:content";

export async function getStaticPaths(): Promise<Object> {
const allPosts:CollectionEntry<"blog">[] = await getCollection("blog");
const uniqueTags: string[] = [
...new Set(allPosts.map((post) => post.data.tags).flat()),
];

return uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post) =>
post.data.tags.includes(tag),
);
//the params tag here is the name of each dynamic route
//the props variable is the data to pass to each page
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}

// What is going on here??
// How can I type both variables
const { tag } = Astro.params;
const { posts } = Astro.props;

// The following is the layout of what will apear on the dynamicly made static paths

<BaseLayout title=My ${tag} Blog Posts , heading=Only ${tag}>
<p class="text-lg font-medium">Posts tagged with {tag}</p>
<ul>
{
posts.map((post) => (
<Formatter
url={/blog/${post.slug}/}
draft={post.data.draft}
title={post.data.title}
pubDate={post.data.pubDate}
tags={post.data.tags}
/>
))
}
</ul>
</BaseLayout>

waxen epoch
#

Most of the time, TS will infer the correct types, but sometimes you have to tell it what the type should be. Try to avoid typing return values whenever possible, as they will typically already be the correct type.

Is this your own code, or something copied or AI generated?

I would not do this: getStaticPaths(): Promise<Object>, it should be:

export async function getStaticPaths()
{
...
} satisfies GetStaticPaths

Also, this:

const allPosts:CollectionEntry<"blog">[] = await getCollection("blog");

should just be:

const allPosts = await getCollection("blog");

as allPosts will be typed properly, assuming the collection is defined correctly in scr/content/config.ts. You can hover your mouse over it to see the type.

The Astro.params is coming from your path, so src/pages/[tag]/[slug].astro, or src/pages/blog/[tag].astro, or something similar. However, I can't tell if you actually have [tag] in your route as you did not provide the path to the code you showed.

The Astro.props is what you're passing from your frontmatter code to your html, in this case, posts. If you need to type it, then you can do:

type Props = {
posts: CollectionEntry<"blog">[]
}
const { posts } = Astro.props;

If there's an error in your code, TS will tell you that posts is the wrong type. However, filteredPosts should be the same type as posts, since you are only doing a filter on it. If it's not, you can force it by doing:

const filteredPosts:CollectionEntry<"blog">[] = allPosts.filter((post) =>
      post.data.tags.includes(tag)
    );

but it shouldn't be necessary.

getStaticPaths() will generate static pages for all matching tag and posts from your collection.

icy violet
# waxen epoch Most of the time, TS will infer the correct types, but sometimes you have to tel...

Hello Henri thank you for taking the time to look at my code and giving me a detailed reply, it did help me out.

This is all my own code, although this is my first every project with typescript so the getStaticPaths(): Promise<Object> you mentioned was just me typing in a placeholder while reading the typescript docs. I meant to take that out, but clearly I forgot. Turns out when I removed it that incorrect type "posts" posts.map((post) => ( in the page template then becomes typed correctly! (It goes from type never to the real type defined in config.ts)

Is the reason why I don't need to explicitly type const allPosts = await getCollection("blog"); because Astro does it for me in the background of the 'getCollection' function? I was under the impression that while I define the type in 'config.ts' in the content folder I still needed to say when a const was going use that type.

Regardless, thank you again for helping me out.