tracking for shynet analytics when Javascript is disabled Adding types to your Neovim configuration — Hugo Sum
Hugo Sum

Adding types to your Neovim configuration

Once again I have started customizing my Neovim’s configuration, so that I can improve my productivity and enjoy every single click on my keyboard at work. After spending hours updating my init.lua and checked out some plugins I have never heard of, I noticed quite a few plugins now include an annotation comment @type for typing their configuration’s arguments.

~/.config/nvim/init.lua
require("lazy").setup({
    spec = {
        {
          "folke/snacks.nvim",
          priority = 1000,
          lazy = false,

          ---@type snacks.Config
          opts = {

          },
        }
    };
})

As Lua is a weakly-typed language, just like Javascript, adding type support to it is a non-trivial task. Instead of replacing Lua with a typed superset, for example Teal, using annotations is a much more straight-forward approach, as users do not need to rewrite their code and adapt a building process that compiles codes from a typed variant back to Lua. They only need to set up LSP diagnostics support with the editor to enjoy those typings. I believe that is the right approach for improving Lua, given its relative small community.

Rather than getting type suggestions from lua-language-server after adding the annotation, I got the following LSP warning.

Undefined type or alias `snacks.Config`. Lua Diagnostics. (undefined-doc-name)

I tried to require snacks before that type annotation, but lua-language-server still refused to pick up the type. At the end, I found out that I have to update the configuration of lua-language-server, so it can analyze all libraries that are imported in a workspace. As I am using lazy.nvim to manage my plugins, all plugins are installed at $XDG_DATA_HOME/nvim/lazy, so I just need to add that path to the configuration.

~/.config/nvim/init.lua
require("lazy").setup({
    spec = {
        {
            "neovim/nvim-lspconfig",
            version = "1.6.0",
            event = { "BufReadPre", "BufNewFile" },
            config = function()
                local lspconfig = require("lspconfig")

                lspconfig.lua_ls.setup({
                    capabilities = capabilities,
                    on_init = function(client)
                        client.server_capabilities.semanticTokensProvider = nil

                        if client.workspace_folders then
                            local path = client.workspace_folders[1].name
                            if
                                path ~= vim.fn.stdpath("config")
                                and (
                                    vim.loop.fs_stat(path .. "/.luarc.json")
                                    or vim.loop.fs_stat(path .. "/.luarc.jsonc")
                                )
                            then
                                return
                            end
                        end

                        client.config.settings.Lua = vim.tbl_deep_extend(
                            "force",
                            client.config.settings.Lua,
                            {
                              runtime = {
                                version = "LuaJIT",
                              },
                              workspace = {
                                checkThirdParty = false,
                                library = {
                                  "$VIMRUNTIME",
                                  "$XDG_DATA_HOME/nvim/lazy", 
                                  "${3rd}/luv/library",
                              },
                            },
                        })
                    end,
                })
            end,
        },
    },
})

After updating my configuration, the LSP is able to pick up types and give suggestions, even without an explicit type annotation.

Avoid global configuration with .luarc.json

Configuration for lua-language-server in init.lua is global, and it will load and analyze types from $XDG_DATA_HOME/nvim/lazy, even if your project does not require any module from it, slowing down the LSP server start up time. A better option is to use .luarc.json to customize lua-language-server. By creating this configuration file at the root level of your project, only libraries specific to that project will be analyzed.

~/.config/nvim/.luarc.json
{
  "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
  "workspace.library": ["$VIMRUNTIME", "$XDG_DATA_HOME/nvim/lazy", "${3rd}/luv/library"]
}

Hugo Sum

A Hongkonger living in the UK. The only thing I know is there is so much I don't know.

Enjoy reading my blog?

Subscribe to a monthly newsletter and join my adventure on software development.

© 2025 Hugo Sum. All Rights Reserved.