Astro のプロジェクトでは、.astro ファイルの型検査に astro check を使います。TypeScript 本体の tsc は .astro を読めないからです。
TypeScript 7 は Go で書き直された新しい tsc で、速さが売りです。ただ 7.0 では、astro check は TypeScript 7 を使えませんでした。2026年10月20日に出た TypeScript 7.1 Beta では、tsc 側に content mapper という仕組みが入り、.astro を tsc で読む道ができました。この記事では、Astro 用の mapper を実際の Astro サイトに入れて、tsc だけで型検査できるか、astro check と何が違うかを確かめます。
content mapper とは#
content mapper は、TypeScript 7.1 で入った新しい拡張の仕組みです。.ts 以外のファイル(.astro、.vue、.svelte など)を、TypeScript が読める形に変換して渡します。TypeScript の発表では、Astro、Vue、Svelte、MDX などで試作や実装が進んでいると紹介されています。
Astro 用は @astrojs/ts-content-mapper というパッケージです。2026年9月に Astro のリポジトリに追加され、README に使い方が書かれています。全体は図1のように動きます。
図1 TypeScript 7.1 の tsc が .astro を型検査する流れ
mapper は node_modules にあるプログラムで、Astro の mapper はパッケージの設定で node から dist/server.js を実行するように指定されています。TypeScript の発表は、content mapper を「実質的に任意の実行ファイル」と説明しています。そのため tsc は、--runExternalCode というフラグで明示的に許可したときだけ mapper を動かします。node_modules のコードが動くので、信頼できるパッケージだけで使ってください。
この記事で使う用語#
| 言葉 | 何をするものか | いつ使うか |
|---|---|---|
astro check | Astro が用意する型検査のコマンド。.astro と .ts をまとめて検査する | 今の Astro プロジェクトの標準。TypeScript 6 までが対象 |
tsc | TypeScript 本体のコンパイラ。--noEmit を付けると、出力せずに型検査だけをする | .ts の型検査。7.1 からは mapper 経由で .astro も読める |
| content mapper | .ts 以外のファイルを TypeScript が読める形に変換するプログラム | tsconfig.json の contentMappers に登録する |
--runExternalCode | mapper の実行を許可する tsc のフラグ | mapper を使うときは必須 |
astro sync | astro:content などの型定義を .astro/ に作るコマンド | astro check は内部で実行する。tsc は実行しない |
何に困るのか#
TypeScript 7 の tsc は速いので、CI の型検査を astro check から tsc に切り替えたくなります。
ところが、tsconfig.json に何も足さずに tsc --noEmit を実行すると、.astro は1つも読まれません。型の誤りを入れた .astro を置いても、エラーが1つも出ないまま exit 0 で終わりました。これでは .astro に誤りがあっても CI は通ります。今回いちばん注意したい点です。
検証の準備#
このブログ自体の Astro サイトを使いました。.astro は18ファイルの小さなサイトです。検証は使い捨ての Docker コンテナで行い、TypeScript 6 と 7.1 Beta は別々の実行で試しました。
| 項目 | 版 |
|---|---|
| Node.js | 22.22.2(Linux aarch64) |
| Astro | 7.4.1 |
| 比較用 | @astrojs/check 0.9.10、TypeScript 6.0.3 |
| 検証対象 | TypeScript 7.1.0-beta、@astrojs/ts-content-mapper 0.2.0 |
| パッケージマネージャー | pnpm 11.28.5 |
元に戻せるよう、別のブランチで試してください。TypeScript を 7.1 Beta に入れ替え、mapper を追加します(-E は版を固定する指定です)。
pnpm add -D -E [email protected] @astrojs/[email protected]
tsconfig.json に足すのは contentMappers だけです。ほかの項目はそのままでかまいません。
{
"contentMappers": [
{
"package": "@astrojs/ts-content-mapper",
"extensions": [".astro"]
}
]
}
この項目は、TypeScript 6 のままでも残しておけます。TypeScript 6 で contentMappers を書いたまま試すと、astro check も TypeScript 6 の tsc もエラーなしで通りました。元に戻すときは、typescript の版を戻して mapper を外せば足ります。
型検査のコマンドです。astro sync を先に実行する理由は後で説明します。
pnpm exec astro sync && pnpm exec tsc --noEmit --runExternalCode
動くか#
動きました。エラーのないサイトでは tsc が exit 0 で終わり、astro build も通りました。
一方、typescript を 7.1 Beta にすると、astro check は検査せずに exit 1 で終わります。表示は TypeScript 7.0 となっていますが、7.1 Beta でもこのとおりでした。
[ERROR] [check] astro check does not currently support TypeScript 7.0. To continue using astro check, install TypeScript 6 instead.
astro check will be deprecated in a future Astro release. Experimental support for type-checking Astro files with TypeScript 7.1+ is available through @astrojs/ts-content-mapper. See its README for setup instructions:
https://github.com/withastro/astro/tree/main/packages/language-tools/ts-content-mapper#usage
このメッセージには、astro check は将来の Astro で非推奨になる予定だとも書かれています。typescript を 7.1 にしたプロジェクトでは、astro check は使えません。
設定を間違えたときの動き#
| 状態 | 結果 |
|---|---|
contentMappers を登録していない | 何も出ずに exit 0(.astro は1つも読まれない) |
登録したが --runExternalCode を付けない | error TS18068: Content mappers require the '--runExternalCode' command line flag to be enabled.、exit 2 |
| mapper のパッケージが入っていない | error TS18075: The content mapper package '...' could not be resolved.、exit 2 |
登録したのにフラグを忘れた場合と、パッケージがない場合は、エラーで止まります。困るのは1行目の、登録自体を忘れた場合だけです。
tsc に --listFiles を付けると、読んだファイルの一覧が出ます。登録しないと .astro は0件、登録すると今回は27件(わざと誤りを入れた3ファイルを含む)でした。CI では、この件数が0でないことを確かめると、登録漏れに気づけます。
pnpm exec tsc --noEmit --runExternalCode --listFiles | grep -c '\.astro$'
astro check と比べる#
わざと誤りを入れた .astro と .ts を16種類用意し、astro check(TypeScript 6)と tsc(TypeScript 7.1 Beta)の両方で検査しました。下の表の上9行は、構文の誤りを含まないのでまとめて実行し、残りの7行は1ファイルずつ実行しました。
| 入れた誤り | astro check | tsc + mapper |
|---|---|---|
frontmatter の型の誤り(const n: number = 'x') | エラー | 同じ行と列で同じエラー |
| テンプレートで未定義の変数を使う | エラー | 同じ |
| コンポーネントの Props の型違い | エラー | 同じ |
| 必須の Props がない | エラー | 同じ |
| 存在しない Props を渡す | エラー | 同じ |
HTML の属性の型違い(href={123}) | エラー | 同じ |
<script> の中の型の誤り | エラー | 同じ |
Astro.props の打ち間違い | エラー | 同じ |
.ts から .astro を import して誤った型で使う | エラー | 同じ |
getCollection に存在しないコレクション名 | エラー | エラー |
| frontmatter の構文の誤り | エラー | エラー |
| タグの閉じ忘れ | エラー | エラー |
is:inline の <script> に型注釈を書き、変数を使わない | エラーと未使用の指摘 | エラーだけ |
| 未使用の変数 | 未使用の指摘 | 何も出ない |
define:vars を使う(誤りなし) | 何も出ない | 何も出ない |
.astro コンポーネントに client:load を付ける | 何も出ない | 何も出ない |
型の誤りは、行と列まで同じでした。たとえば frontmatter の誤りは、どちらも2行目7列を指しました。
src/pages/_lab/01-frontmatter.astro(2,7): error TS2322: Type 'string' is not assignable to type 'number'.
Astro の構文の誤りは、tsc では astro1000 というコードで出ます。
src/pages/_lab/23-unclosed-tag.astro(3,11): error astro1000: Expected a matching closing tag but instead found '</div>'.
違いは未使用の変数の指摘#
結果が違った2種類は、どちらも未使用の変数を指摘するかどうかの差でした。
astro check は未使用の変数を warning ts(6133) と表示します。最後の集計では警告ではなくヒントとして数えられ、exit 0 のままです。tsc は何も出しません。tsc で同じことをしたいときは、--noUnusedLocals を付けるか、tsconfig.json で noUnusedLocals を有効にします。その場合はエラーになり、exit 2 で止まります。
構文の誤りがあると、型の誤りが出ないことがある#
表とは別に、誤りを入れたファイルをまとめて実行したときに気づいた動きです。
タグの閉じ忘れがあるファイルと、frontmatter に型の誤りがあるファイルを一緒に置くと、tsc は構文の誤りだけを出して exit 2 で終わり、型の誤り(TS2322)は出しませんでした。astro check は両方を出しました。私も最初は、7種類をまとめて実行したときに getCollection の誤りが tsc で出なかったので、機能の差だと思いかけました。1ファイルずつ試すと出ました。
試した組み合わせは少ないので、いつもこうなるとは言えません。構文の誤りを直したら、もう一度実行して型の誤りを確かめてください。
astro sync を先に実行する#
astro check は、検査の前に astro sync を自動で実行します。tsc は実行しません。
.astro/ フォルダがない状態(リポジトリをクローンした直後や、CI の初回)で tsc を実行すると、astro:content などが見つからずに失敗しました。
src/content.config.ts(1,34): error TS2307: Cannot find module 'astro:content' or its corresponding type declarations.
astro sync の後に実行すると通りました。CI では astro sync && tsc --noEmit --runExternalCode のように続けて実行します。
速度#
5回ずつ測った中央値です。どれも同じイメージのコンテナで測りましたが、表の行によっては別の回の実行です。pnpm exec を挟まず、node_modules/.bin のコマンドを直接実行しました。
| コマンド | 中央値 |
|---|---|
astro check(TypeScript 6) | 2.72 秒 |
astro check --noSync(TypeScript 6) | 2.21 秒 |
astro sync && tsc --noEmit --runExternalCode | 1.03 秒 |
tsc --noEmit --runExternalCode(同期なし) | 0.39 秒 |
同期を含めた比較では、2.72 秒が 1.03 秒になりました。astro sync だけを別に測ると約 0.8 秒でした。.astro が18ファイルの小さなサイトでの値なので、大きなプロジェクトで同じ比率になるとは限りません。
今すぐ置き換えるべきか#
本番の CI は、まだ astro check と TypeScript 6 のままにするのが無難です。
- TypeScript 7.1 はまだ Beta です。発表では、RC は約1か月後、その後に正式版の予定です
- Astro の TypeScript のガイドには、まだ mapper の手順がありません。mapper を追加した Pull Request には、TypeScript 7.1 が正式に出て移行するまで E2E テストを追加できないと書かれています
- VS Code などのエディタでの連携は、Astro 側の Pull Request がまだ取り込まれていません(2026年11月7日時点)
試す価値はあります。今回の範囲では型の誤りの検出は astro check と同じで、速度も上がりました。試すときは別のブランチで、次の3点を確かめてください。
contentMappersを登録する。--listFilesで.astroの件数が0でないことを見ると、登録漏れに気づけるastro syncを先に実行する- 未使用の変数も見たいなら
--noUnusedLocalsを付ける
まとめ#
TypeScript 7.1 Beta の tsc は、Astro の content mapper を使うと .astro を型検査でき、型の誤りの検出は今回の範囲で astro check と同じでした。ただし、mapper を登録し忘れると .astro を1つも読まずに exit 0 で通ります。正式版とエディタ対応がそろうまでは、別のブランチで試しながら、本番は astro check を使い続けるのがよいと思います。