## 経緯
[[TypeScript 7]]こと[[tsgo]]を以前に試した。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" />
<span class="link-card-v2-site-name">Minerva</span>
</div>
<div class="link-card-v2-title">
📜2025-12-04 TypeScript 7 Previewをふたたび試す
</div>
<div class="link-card-v2-content">半年前に試したTypeScript 7 Previewとtsgoの導入経緯を踏まえ、NeovimのLSP設定やグローバルインストールを行い挙動を検証したが、補完やCode Actionの差異からNeovim利用は保留し、Nuxt 4ではvue-tsc未対応のためtypecheck移行は困難と判断した経過を記録した</div>
<img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/activity.webp" />
<a data-href="📜2025-12-04 TypeScript 7 Previewをふたたび試す" class="internal-link"></a>
</div>
%%[[📜2025-12-04 TypeScript 7 Previewをふたたび試す]]%%
ただ、このときは[[Nuxt4]]([[Vue]])のコードを解析することができなかった。[[Vue Language Tools]]のissueにも上がっていた。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
TypeScript Native / TypeScript 7 (`@typescript/native-preview`, `tsgo`) Support · Issue #5381 · vuejs/language-tools
</div>
<div class="link-card-v2-content">
What problem does this feature solve? TypeScript on Go (also known as TypeScript 7 or TypeScript Native) has bee ...
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/7daf42becffbd7367a2594013738056d8905b5d0f365fdde912563ef805a6bef/vuejs/language-tools/issues/5381" />
<a href="https://github.com/vuejs/language-tools/issues/5381"></a>
</div>
最近これを見返したらこんな投稿があった。
> If you just want a faster cli: [https://github.com/KazariEX/vue-tsgo](https://github.com/KazariEX/vue-tsgo)
ということで早速試してみた。
## プロジェクト作成
前回同様に[[Nuxt4]]向けプロジェクトを[[🦉Toki]]でつくる。
```console
toki nuxt nuxt4-sandbox
cd nuxt4-sandbox
bun dev -o
```
```
$ bun pm ls
/Users/tadashi-aikawa/tmp/nuxt4-sandbox node_modules (731)
├── @fsouza/
[email protected]
├──
[email protected]
├──
[email protected]
├──
[email protected]
├──
[email protected]
└──
[email protected]
```
## コード編集
`app/app.vue`
```html
<script setup lang="ts">
const cnt = ref<string>(0);
const increment = () => {
cnt.value++;
};
</script>
<template>
<div>
<button @click="increment">Increment</button>
<p>Count: {{ cnt }}</p>
<div>
</div>
</template>
```
このコードには3つの問題がある。
```error
~/tmp/nuxt4-sandbox/app/ 3
└╴ app.vue 3
├╴● Argument of type 'number' is not assignable to parameter of type 'string'. ts-plugin (2345) [2, 27]
├╴● An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type. ts-plugin (2356) [4, 3]
└╴● Element is missing end tag. vue (24) [9, 3]
```
`bun tsc` コマンドは[[Vue SFC]]に未対応なのでエラーを出さない。
```console
$ bun tsc
```
`bun nuxt typecheck` ではちゃんとエラーになる。
```error
$ bun nuxt typecheck
app/app.vue:2:27 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
2 const cnt = ref<string>(0);
~
app/app.vue:4:3 - error TS2356: An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.
4 cnt.value++;
~~~~~~~~~
Found 2 errors.
ERROR Process exited with non-zero status (2) 11:32:12 AM
at G._waitForOutput (node_modules/tinyexec/dist/main.js:574:92)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async Object.run (node_modules/@nuxt/cli/dist/typecheck-C8wVgBzd.mjs:66:3)
at async runCommand (node_modules/citty/dist/index.mjs:196:47)
at async runCommand (node_modules/citty/dist/index.mjs:193:21)
at async runMain (node_modules/citty/dist/index.mjs:304:10)
error: "nuxt" exited with code 1
```
[[tsgo]]は[[tsc]]に対応するものなのでエラーが出ないのは当然だが、`bun nuxt typecheck` 同等のことがしたい。
## [[Vue Tsgo]]のインストール
```console
bun add -D vue-tsgo
```
```console
$ bun vue-tsgo --version
v0.0.1-yggdrasill.11
```
## 実行してみる
```console
bun vue-tsgo --project .nuxt/tsconfig.app.json
```
エラーになった。
```error
[Vue] Failed to resolve the path of tsgo. Please ensure the @typescript/native-preview package is installed.
error: "vue-tsgo" exited with code
```
どうやら [[pnpm]] じゃないと `catalog` が認識されなそう。。出直す
## プロジェクト再作成
```console
toki nuxt-pnpm nuxt-pnpm-sandbox
cd nuxt-pnpm-sandbox
pnpm dev -o
```
コードも[[#コード編集]]の状態にしておく。
```console
pnpm nuxt typecheck
```
```error
npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm.
app/app.vue:2:27 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
2 const cnt = ref<string>(0);
~
app/app.vue:4:3 - error TS2356: An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.
4 cnt.value++;
~~~~~~~~~
Found 2 errors.
ERROR Process exited with non-zero status (2) 11:57:44 AM
```
## [[Vue Tsgo]]の再インストールと再実行
```console
pnpm i -D vue-tsgo
```
```console
$ pnpm list
Legend: production dependency, optional only, dev only
nuxt-pnpm-sandbox /Users/tadashi-aikawa/tmp/nuxt-pnpm-sandbox (PRIVATE)
dependencies:
nuxt 4.3.1
vue 3.5.28
vue-router 4.6.4
devDependencies:
@fsouza/prettierd 0.27.0
prettier-plugin-organize-imports 4.3.0
vue-tsgo 0.0.1-yggdrasill.11
```
実行してみたが...
```console
$ pnpm vue-tsgo --project .nuxt/tsconfig.app.json
[Vue] Failed to resolve the path of tsgo. Please ensure the @typescript/native-preview package is installed.
```
これは[[pnpm]]でもいっしょぽい。 `@typescript/navtive-preview` は必要そう。
## [[TypeScript 7]]をインストール
```console
pnpm add -D @typescript/native-preview
```
```console
$ pnpm list
Legend: production dependency, optional only, dev only
nuxt-pnpm-sandbox /Users/tadashi-aikawa/tmp/nuxt-pnpm-sandbox (PRIVATE)
dependencies:
nuxt 4.3.1
vue 3.5.28
vue-router 4.6.4
devDependencies:
@fsouza/prettierd 0.27.0
@typescript/native-preview 7.0.0-dev.20260209.1
prettier-plugin-organize-imports 4.3.0
vue-tsgo 0.0.1-yggdrasill.11
```
## 再び実行
```console
$ pnpm vue-tsgo --project .nuxt/tsconfig.app.json
```
configが見つからなそう。
```error
error TS18003: No inputs were found in config file '/Users/tadashi-aikawa/tmp/nuxt-pnpm-sandbox/node_modules/.ca ... 中略
Found 1 error
```
[[チャッピー]]に聞く。
> [!left-bubble] ![[chappy.webp]]
> 原因と対処の整理です。
>
> 結論
> vue-tsgo が一時ワークスペースを node_modules/.cache/vue-tsgo/... に作る仕様のため、tsgo(TypeScript 7)が node_modules 配下をワイルドカード探索から除外してしまい、結果として include が 0 件になって TS18003 が出ています。
>
> 根拠(手元確認)
>
> - 実際の tsgo 用 tsconfig は node_modules/.cache/vue-tsgo/4d05ead6/.nuxt/tsconfig.app.json に生成されており、include は node_modules/.cache/vue-tsgo/4d05ead6/app/**/* などです。
> - node_modules/.cache/vue-tsgo/4d05ead6/app/app.vue.ts などの生成ファイルは存在しますが、tsgo が node_modules を無視しているため “No inputs” になります。
>
> 対処(コード変更なしで可能)
> vue-tsgo のキャッシュ先を node_modules 外に出します。
> empathic/package の CACHE_DIR で上書きできます。
>
> 例(ワンショット実行)
>
> CACHE_DIR=.cache pnpm vue-tsgo --project .nuxt/tsconfig.app.json
>
> 例(/tmp でも可)
>
> CACHE_DIR=/tmp/vue-tsgo-cache pnpm vue-tsgo --project .nuxt/tsconfig.app.json
>
> 恒久化したい場合
> package.json の script に CACHE_DIR=.cache を足すやり方が安全です。
> 必要ならこちらで変更しますが、実装扱いになるので、プラン提示→承認後に進めます。
両方ためしたけど何も変わらない。。
```console
CACHE_DIR=.cache pnpm vue-tsgo --project .nuxt/tsconfig.app.json
CACHE_DIR=/tmp/vue-tsgo-cache pnpm vue-tsgo --project .nuxt/tsconfig.app.json
```
改めて投げる。
> [!left-bubble] ![[chappy.webp]]
> 原因がはっきりしました。
> CACHE_DIR を絶対パスにしても TS18003 が出るのは、vue-tsgo が生成する tsconfig の include が壊れているためです。
>
> 具体的には、生成された tsconfig.app.json がこうなっています(実ファイル確認済み):
>
> - include に .../.cache/vue-tsgo/app/**/* が入っている
> しかし実際の生成ファイルは .../.cache/vue-tsgo/4d05ead6/app/app.vue.ts
> - include に .../.cache/vue-tsgo/4d05ead6/nuxt.d.ts が入っている
> 実際は .../.cache/vue-tsgo/4d05ead6/.nuxt/nuxt.d.ts
>
> つまり パスがずれているので 1 件もマッチせず TS18003 になります。
> これは vue-tsgo が .nuxt/tsconfig.app.json の include を再マップする際に、./ と ../ を誤って解釈しているのが原因です(上流バグ)。
>
> ## 回避策(確実)
>
> ../ を含まない include を持つ専用 tsconfig を作ると動きます。
> 例: tsconfig.vue-tsgo.json を作って extends し、include をプロジェクトルート基準に置き換えます。
>
> 提案プラン(実装扱いなので承認が必要です)
>
> Plan
>
> 1. tsconfig.vue-tsgo.json を作成
> - extends: "./.nuxt/tsconfig.app.json"
> - include をプロジェクトルート基準に置換(app/**/*, .nuxt/**/*.d.ts, modules/*/runtime/**/* など)
> 2. pnpm vue-tsgo --project tsconfig.vue-tsgo.json で実行
> 3. 必要なら package.json にスクリプト追加
>
本当かよく分からんけど、とりあえず任せてみる。[[Vue Tsgo]]専用の設定ができること自体は違和感ないので。
変わらずだったので返答。
> [!left-bubble] ![[chappy.webp]]
> 原因は CACHE_DIR を相対パスにしたことで、生成された tsconfig の include が キャッシュ内で二重に解釈されてしまう点でした。
> CACHE_DIR を 絶対パスにすると解消します。
>
> 実行コマンド(これで通ります)
>
> CACHE_DIR="$PWD/.cache" pnpm vue-tsgo --project tsconfig.vue-tsgo.json
```console
$ CACHE_DIR="$PWD/.cache" pnpm vue-tsgo --project tsconfig.vue-tsgo.json
app/app.vue:2:27 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
2 const cnt = ref<string>(0);
~
app/app.vue:4:3 - error TS2356: An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.
4 cnt.value++;
~~~~~~~~~
Found 2 errors in the same file, starting at: app/app.vue:2
```
出た👏
## 必要だったこと
### 専用の設定ファイル
`tsconfig.vue-tsgo.json`
```json
{
"extends": "./.nuxt/tsconfig.app.json",
"include": [
".nuxt/nuxt.d.ts",
"app/**/*",
"modules/*/runtime/**/*",
"test/nuxt/**/*",
"tests/nuxt/**/*",
"layers/*/app/**/*",
"layers/*/modules/*/runtime/**/*",
"shared/**/*.d.ts",
"modules/*/shared/**/*.d.ts",
"layers/*/shared/**/*.d.ts",
"*.d.ts",
"layers/*/*.d.ts"
]
}
```
### キャッシュディレクトリ指定のコマンド
```console
CACHE_DIR="$PWD/.cache" pnpm vue-tsgo --project tsconfig.vue-tsgo.json
```
`package.json` にコマンドを書いておくとよい。
```json
{
"scripts": {
"nuxt:typecheck": "nuxt typecheck",
"typecheck": "CACHE_DIR=$PWD/.cache vue-tsgo --project tsconfig.vue-tsgo.json",
},
}
```
## 実行速度の違い
一番期待しているのはここだったので確認する。
```console
pnpm nuxt:typecheck 2.98s user 0.51s system 129% cpu 2.695 total
```
```console
pnpm typecheck 0.69s user 0.17s system 217% cpu 0.396 total
```
めっちゃ良い。このボリュームでも既に快適さが違う。
`nuxt typecheck`が遅すぎたので、[[overseer.nvim]]の[[trouble.nvim]]連携は解除していたけど、このスピード感が実際のプロダクトでも出るなら復活も選択肢に入る。
## [[🦉Bruno Report Viewer]]で試す
大きなプロジェクトではないが、[[Vue]]を使っていてアクティブなプロダクトがこれしかなかったので...。ただ、うまくいかなかった。(エラーがでない。)
> [!left-bubble] ![[chappy.webp]]
> 結論から言うと、差分の原因は -b(build mode)である可能性が高く、現時点の vue-tsgo では同等のことはできません。理由は2点あります。
> (typescriptlang.org (https://www.typescriptlang.org/docs/handbook/project-references))
>
> 1. vue-tsc -b は TypeScript の build mode で、references を辿って関連プロジェクトを順序付きでビルド/型チェックします。
> --project 単体では参照先は自動でビルドされないため、-b の有無で拾えるエラーが変わります。(typescriptlang.org (https://www.typescriptlang.org/docs/handbook/project-references))
> 2. vue-tsgo は内部的に tsgo を使って型チェックするツールです。(github.com (https://github.com/KazariEX/vue-tsgo))
> tsgo 自体が現時点のプレビューで --build(= -b)を未サポート と明言されています。
> そのため vue-tsgo も -b 相当ができません。(devblogs.microsoft.com (https://devblogs.microsoft.com/typescript/announcing-typescript-native-previews/))
>
> 実務的な対処案(どれか1つでOK)
>
> 1. -b を使いたい場合は当面 vue-tsc -b を使う(CI/本番はこれが安全)。(typescriptlang.org (https://www.typescriptlang.org/docs/handbook/project-references))
> 2. vue-tsgo で近いことをするなら、参照先を個別に回す。
>
> bun vue-tsgo --project tsconfig.app.json
> bun tsgo --project tsconfig.node.json
>
> 3. まず差分を切り分けたいなら、-b なしの vue-tsc と比較してみる。
>
> bun vue-tsc --project tsconfig.app.json
>
> この結果でエラーが出るなら、tsgo 側の差分(未対応やバグ)の可能性が高いです。(devblogs.microsoft.com (https://devblogs.microsoft.com/typescript/announcing-typescript-native-previews/))
>
> もし「vue-tsc -b のどのプロジェクトでエラーが出ているか」を確認したい場合は、--verbose 付きで見ると分かります。(typescriptlang.org (https://www.typescriptlang.org/docs/handbook/project-references))
>
> 必要なら、出ているエラーと対象ファイルを教えてくれれば「-b 由来か」「tsgo 由来か」を具体的に切り分けます。
御意。ただ、[[vue-tsc]]で全く同じコマンドを打つとエラーがちゃんと出るので、これは[[Vue Tsgo]]との単なる差分かもしれない...。
```console
$ bun vue-tsgo --project tsconfig.app.json
$ bun vue-tsc --project tsconfig.app.json
src/components/side/TheSideContents.vue:37:10 - error TS2367: This comparison appears to be unintentional because the types 'BRVStatus' and '"passed2"' have no overlap.
37 (status === "passed2" && filterPassed.value) ||
~~~~~~~~~~~~~~~~~~~~
Found 1 error in src/components/side/TheSideContents.vue:37
error: "vue-tsc" exited with code 2
```
> [!left-bubble] ![[chappy.webp]]
> • 原因は src/App.vue ではなく、vue-tsgo 側の挙動です。再現確認した結果は以下です。
>
> - bun vue-tsgo --project tsconfig.app.json は EXIT:0 で無出力。
> - しかし bunx vue-tsc -p tsconfig.app.json --noEmit だと src/App.vue:21 の型エラーを正しく検出します。
> - TS2345: Argument of type 'string' is not assignable to parameter of type 'number'
> - vue-tsgo が生成した仮想ファイルでは、v-model="... as string" 系が __VLS_ctx.xxx as string = $event; になっており、TS1005(構文エラー)を出しています。
> - 例: node_modules/.cache/vue-tsgo/206bb4ea/src/components/main/HeadersTable.vue.ts:127
> - その後 vue-tsgo の診断マッピング処理で、マップできない診断を削除してしまうため、最終的に「エラーなし」になります。
> - node_modules/vue-tsgo/dist/project-7AwPhiYv.mjs:2999
> - node_modules/vue-tsgo/dist/project-7AwPhiYv.mjs:3007
>
> 結論として、今の組み合わせでは vue-tsgo が false negative を出しており、App.vue の型エラーが見えません。
> 当面は型チェックを vue-tsc (bun run typecheck / bunx vue-tsc ...) に戻すのが安全です。必要なら、vue-tsgo 回避のための最小修正案(v-model の as を避ける等)をプラン化して出します。
> [!left-bubble] ![[chappy.webp]]
> • その疑問は正しくて、通常なら「別エラーは別で出る」はずです。
> 今回消えているのは、vue-tsgo の処理が 2 段あるためです。
>
> 1. tsgo 実行段階
> v-model="... as string" 由来で生成された仮想コードに構文エラー(例: node_modules/.cache/vue-tsgo/206bb4ea/src/components/main/HeadersTable.vue.ts:127 の TS1005)が出ます。
> この時点で tsgo 側はその構文エラー群しか出さず、App.vue の型エラー(ref\<number\>('hoge'))は出てきませんでした。
> 2. vue-tsgo の再マップ段階
> vue-tsgo は仮想ファイルの診断を元 .vue にマップできないと削除します(node_modules/vue-tsgo/dist/project-7AwPhiYv.mjs:3007 の diagnostics.splice(i--, 1))。
> TS1005 は生成コード由来でマップ不能なので全削除され、最終的に 0 件になります。
>
> つまり「他のエラーまで消した」のではなく、
>
> - まず tsgo が他のエラーを出していない
> - 出たエラーは vue-tsgo がマップ不能として捨てる
> この合成で false negative になっています。
たしかに `as string` をすべて削除したらエラーが表示された。ちなみにパフォーマンスの違いは2.5倍くらい。
```console
bun vue-tsgo --project tsconfig.app.json 0.79s user 0.16s system 261% cpu 0.361 total
bun vue-tsc --project tsconfig.app.json 2.02s user 0.13s system 212% cpu 1.013 total
```
## 結論
### まとめ
- [[Nuxt4]]の空プロジェクトでも4~5倍高速
- シンプルな構成だとそこまで差分は出ない
- 実際のプロダクトでどれくらい差が出るかはウォッチしたい
- 🚨 **[[v-model]]で[[型アサーション (TypeScript)|as cast]]をしていると結果が空(問題なし)になる**
### [[Nuxt4]]プロジェクトへの導入方法
```console
pnpm add -D vue-tsgo @typescript/native-preview
```
`tsconfig.vue-tsgo.json`
```json
{
"extends": "./.nuxt/tsconfig.app.json",
"include": [
".nuxt/nuxt.d.ts",
"app/**/*",
"modules/*/runtime/**/*",
"test/nuxt/**/*",
"tests/nuxt/**/*",
"layers/*/app/**/*",
"layers/*/modules/*/runtime/**/*",
"shared/**/*.d.ts",
"modules/*/shared/**/*.d.ts",
"layers/*/shared/**/*.d.ts",
"*.d.ts",
"layers/*/*.d.ts"
]
}
```
```json
{
"scripts": {
"typecheck": "CACHE_DIR=$PWD/.cache vue-tsgo --project tsconfig.vue-tsgo.json",
},
}
```
### [[Vue]]プロジェクトへの導入方法
```console
pnpm add -D vue-tsgo @typescript/native-preview
```
```json
{
"scripts": {
"typecheck": "vue-tsgo --project tsconfig.app.json",
},
}
```