Astro の Content Collections で動的ルーティングを実装するときの型指定

## この記事の概要

[Astro のバージョン 2 で追加された Content Collections](https://docs.astro.build/ja/guides/content-collections/) において動的ルーティングを実装したら型のエラーが出たので調べました。

主に自分の備忘録のための記事です。

## 環境

Astro のバージョンは 2.10.7 です。

## 公式ドキュメントの記載

[公式ドキュメント](https://docs.astro.build/ja/guides/content-collections/#building-for-static-output-default)の example コードを引用します。

```jsx:src/pages/posts/[...slug].astro
---
import { getCollection } from 'astro:content';
// 1. Generate a new path for every collection entry
export async function getStaticPaths() {
  const blogEntries = await getCollection('blog');
  return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
  }));
}
// 2. For your template, you can get the entry directly from the prop
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<h1>{entry.data.title}</h1>
<Content />
```

このとき、`getStaticPaths()` の中の `entry` は型が指定されています[^type-of-collection]が

[^type-of-collection]: 型は [src/content/config.ts というファイルの中に指定する](https://docs.astro.build/ja/guides/content-collections/#defining-a-collection-schema)ことで有効になります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/6cc512d4-d915-1fcb-9e46-8636f6287353.png)

`Astro.props` から分割代入した `entry` は `any` になっています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/048e3636-256b-93f1-4860-08f3eee6f685.png)

## 遭遇した問題

記事の中の `frontmatter` に `keywords` という要素を用意していて、型は `string` の配列にしていました。
そして、書いていたコードはこちらです。

```jsx
<ul>
  {entry.data.keywords.map((keyword) => <li>{keyword}</li>)}
</ul>
```

`entry` が `any` になっているので、`keyword` も `any` になり得るとのことでエラーが出ています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/a20cb5b3-837d-718e-ca8d-ba8bbabfb5bb.png)

## 解決策

[公式ドキュメントの TypeScript のセクション](https://docs.astro.build/en/guides/typescript/#infer-getstaticpaths-types)に書いてありました。

```jsx:src/pages/posts/[...slug].astro
---
import { InferGetStaticParamsType, InferGetStaticPropsType, GetStaticPaths } from 'astro';

export const getStaticPaths = (async () => {
  const posts = await getCollection('blog');
  return posts.map((post) => {
    return {
      params: { slug: post.slug },
      props: { draft: post.data.draft, title: post.data.title },
    };
  });
}) satisfies GetStaticPaths;

type Params = InferGetStaticParamsType<typeof getStaticPaths>;
type Props = InferGetStaticPropsType<typeof getStaticPaths>;

const { slug } = Astro.params as Params;
//                      ^? { slug: string; }
const { title } = Astro.props;
//                      ^? { draft: boolean; title: string; }
---
```

`InferGetStaticParamsType` や `InferGetStaticPropsType` を指定することで、`Astro.params` や `Astro.props` に型を当てられました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/351d9110-f98d-5c38-da6d-d8b96e3ae40d.png)

先ほどの `keyword` からもエラーが消えました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/ad9046cc-3b9b-d61d-6669-70a55d3b476d.png)

## 最後に

Astro は割と TypeScript が前提になっている感じだと思っていたのですが、予期せぬエラーが出たときは [TypeScript 🚀 Astro Documentation](https://docs.astro.build/en/guides/typescript/) に目を通すのが良さそうです。

---

最後まで読んでくださってありがとうございます！
[X (Twitter)](https://twitter.com/xrxoxcxox)でも情報を発信しているので、良かったらフォローお願いします！

[Devトーク](https://jobs.qiita.com/employers/qiita-inc/dev_talks/489)でお話してくださる方も募集中です！