Neovim0.11 x tsgoでTSファイルの型チェックをする
この記事はVim駅伝の2025-04-02の記事です。
前回の記事は静カニさんのvimmerになったです。
次回の記事はtadashi-aikawaさんの📘Neovimで最強のダッシュボードをつくってみたです。
MicrosoftがTypeScriptの関連ツールをGOに移植することがニュースになりました。10倍くらい速いらしいです。
この記事では、これをNeovimから呼び出して型チェックします。
こちらのリポジトリで公開されています。直接実行可能な形では提供されていないので、クローンしてきてビルドしましょう。submodulesもあるのでクローンには少し時間を要します。
package.jsonにビルドスクリプトが定義されています。最初はgoの依存をダウンロードするようなので、ここも少し時間がかかります。
$ npm install
$ npm run build
そうすると、built/local/tsgo
に実行ファイルができるので、ここにパスを通しておきます。
型チェック確認用に、こんな感じの型エラーが出るtsファイルを作りました。name
がない可能性があるため、確認なしにtoUpperCase()
を呼んではいけないコードです。
type User = {
name?: string;
}
export function welcome(user: User): string {
return `Welcome ${user.name.toUpperCase()}`;
}
では、tsgoをNeovim builtin lspに設定します。Neovim v0.11でvim.lsp
以下のAPIが整備されたのでそれを使ってみます。こちらのgistと、lspconfigのサンプルを参考にしました。
vim.lsp.config.tsgo = {
cmd = { "tsgo", "lsp", "--stdio" }, -- tsgoにはPATHを通しておく
filetypes = {
'javascript',
'javascriptreact',
'javascript.jsx',
'typescript',
'typescriptreact',
'typescript.tsx',
},
root_markers = { 'tsconfig.json', 'jsconfig.json', 'package.json', '.git' },
-- settings = {} ここに設定項目が入ってくる想定
}
vim.lsp.enable('tsgo')
上記を設定のうえ、問題のファイルを開くとこのように診断が表示されます。無事うごきました。
tsgo-lspの診断結果
続いて、プロジェクト全体の診断も実行してみます。コマンドラインからtsgo
を実行すると、以下のようにエラーが報告されました。これはターミナル上では見やすいものの、エディタに取り込むには適していないため整形が必要です。
$ tsgo
まず-pretty=false
オプションをつけます。エラー箇所を下線で示していた部分がなくなりました。ファイル名などのハイライトも消えています。
$ tsgo -pretty=false
続いてstderrを捨てます。Warning...
の警告がなくなりました。最初の見た目ではわかりませんでしたが、ここはstderrに出ていたんですね。
$ tsgo -pretty=false 2>/dev/null
ファイル数や実行時間のサマリーはオプションでは消せないようだったので、head
で下から12行を吹き飛ばします。
$ tsgo -pretty=false 2>/dev/null | head -n-12
これでファイル名(行,桁): エラーメッセージ
のみを抽出できました。このコマンドとフォーマットをmakeprg
とerrorformat
のオプションに設定すれば、出力を:make
で取り込めます。
こんな感じのコマンドにしました。makeprg
は|
を、errorformat
は,
をエスケープしなければならない点に注意しましょう。ここではmakeprg
/errorformat
を一旦save_
変数に代入して保存していますが、他の場所でこれらを使っていない場合は直接上書きしても問題ありません。
vim.api.nvim_create_user_command('Tsgo', function()
local save_makeprg = vim.opt.makeprg
local save_errorformat = vim.opt.errorformat
vim.opt.makeprg = [[tsgo -pretty=false 2>/dev/null\|head -n-12]]
vim.opt.errorformat = [[%f(%l\,%c): %m]]
vim.cmd('silent! make!')
local size = vim.fn.getqflist({ size = true }).size
if size > 0 then
vim.notify('[tsgo] type-error found', vim.log.levels.WARN)
vim.cmd.copen()
else
vim.notify('[tsgo] type-check passed', vim.log.levels.INFO)
vim.cmd.cclose()
end
vim.opt.makeprg = save_makeprg
vim.opt.errorformat = save_errorformat
end, { desc = 'Tsgo' })
上記の設定を読み込むと、:Tsgo
コマンドが使えるようになります。エラーが検出されるとquickfixが開きます。
以下が実行例です。今回は問題が起きているのが1ファイルしかないのであまり意味はありませんが、動作の雰囲気はわかるかと思います。エラーが複数ファイルで出ていれば、それらをすべてquickfixで一覧できます。
:Tsgo実行例(筆者はquicker.nvimを入れているのでqflistの表示がデフォルトとちょっと違います)
これで数日使ってみたところ、実行が速くて良い感じでした。ただ、途中の実行スクショに出ていますが、まだtsxの対応が不十分なところがあるので、vtslsやtsc.nvimも併用しています。今後のtsgoの発展に期待したいと思います。
以下追記
tsgoが更新されてけっこう変わっていたので書き換えました。
- tsx未対応の警告が消えた
- どうやらサマリーは12行とは限らないらしい
- vim.systemを使ったほうが非同期になってお得
vim.api.nvim_create_user_command('Tsgo', function()
local function on_exit(obj)
if obj.stderr ~= '' then
vim.notify('[tsgo] Error: ' .. obj.stderr, vim.log.levels.ERROR)
return
end
local list = vim.split(obj.stdout, '\n')
list = vim.tbl_filter(function(v)
local t = vim.trim(v)
if t == '' then
return false
end
-- Files: などのサマリー情報を除外
return not vim.regex('^\\s*\\u[[:lower:] ]*: '):match_str(t)
end, list)
if #list > 0 then
vim.notify('[tsgo] type-error found', vim.log.levels.WARN)
else
vim.notify('[tsgo] type-check passed', vim.log.levels.INFO)
end
-- E5560対策
local setqflist = function()
-- set quickfix list
vim.fn.setqflist({}, 'r', { title = 'tsgo', lines = list, efm = [[%f(%l\,%c): %m]] })
vim.cmd.cwindow()
end
vim.defer_fn(setqflist, 100)
end
vim.system({ 'tsgo', '-noEmit', '-pretty=false' }, { text = true }, on_exit)
end, { desc = 'Tsgo' })
defer_fnについてはこちら↓
Discussion