Full TypeScript-grade typing for the Nix language — autocomplete, type errors,
hover, go-to-definition — directly in .nix files with no transpilation step.
Parses and type-checks all 42,298 nixpkgs files in 13 seconds without crashing -- and where types exist or can be automatically inferred by TypeScript, they're correct.
# @ts: { lib: Lib; stdenv: Stdenv; [key: string]: any }
{ lib, stdenv }:
let
version = lib.concatStringsSep "." [ "1" "0" "0" ];
isLinux = stdenv.hostPlatform.isLinux;
greeting = lib.optionalString isLinux "Hello from Linux!";
# ❌ error TS2345: Argument of type 'number' is not assignable
# to parameter of type 'boolean'.
bad = lib.optionalString 42 "oops";
in
stdenv.mkDerivation {
pname = "example";
inherit version;
src = ./.;
}See examples/starter/ for a runnable project.
code --install-extension $(nix build github:ryanrasti/typenix#vscode-extension --print-out-paths)/typenix.vsixPut this in lsp/typenix.lua at the root of your Neovim config:
---@type vim.lsp.Config
return {
cmd = function(dispatchers)
local cmd = "typenix"
return vim.lsp.rpc.start({ cmd, "--lsp", "--stdio" }, dispatchers)
end,
root_markers = { "flake.nix", ".git" },
filetypes = {
"nix",
"nixts",
},
}Then enable it as any other lsp. For nixts filetype for .nix.d.ts files:
vim.filetype.add({
pattern = {
[".*/*.nix.d.ts"] = "nixts",
},
})
vim.treesitter.language.register("typescript", { "nixts" })# needs a tsconfig.json pointing at your .nix files
echo '{"include": ["**/*.nix"]}' > tsconfig.json
nix run github:ryanrasti/typenix -- --noEmitTypeNix is a fork of tsgo
(the TypeScript compiler in Go). When it encounters a .nix file:
- tree-sitter-nix parses it
- The tree-sitter AST is converted into the same TypeScript AST nodes that the TS parser produces
- The standard TypeScript binder, checker, and LSP work essentially unchanged
.nix file → tree-sitter-nix → TS AST → binder → checker → LSP
.ts file → TS scanner/parser → TS AST → binder → checker → LSP
↑
same types, same pipeline
The result: the full TypeScript type system applied to Nix — with special handling for:
- Fixed-point/overrides:
classannotation converts(self: { ... })patterns into a TypeScript class, with full self-referential typing - Existing
::type annotations from nixpkgslib/automatically parsed and used ./foo.nixpaths carry their import type, are hoverable and followable via LSP- Bundled types for
Lib,Stdenv,Platform,Derivation, and allpkgs/by-namepackages
Use # @ts: comments to annotate nix expressions with TypeScript types:
# @ts: { lib: Lib; stdenv: Stdenv; [key: string]: any }
{ lib, stdenv }:Available types (no imports needed):
Nixpkgs— top-level pkgs objectLib— all nixpkgs lib functions (lib.concatStringsSep,lib.optionalString, etc.)Stdenv—mkDerivation,hostPlatform,cc, etc.Platform—isLinux,isDarwin,system, etc.Derivation— standard derivation output type
nix build .#typenix # CLI binary
nix build .#vscode-extension # VS Code extension (includes binary)TypeNix is a proof of concept. It is usable on real nix/nixpkgs code:
- Typing for builtins,
lib,stdenv.mkDerivation,import, many packages on theNixpkgstype - ~50 type errors remaining in nixpkgs itself (down from thousands)
- Fixed-point self-references translated as TypeScript classes allowing for typed self-reference
- VS Code extension with hover, go-to-definition, autocomplete
Limitations:
- Many places in nixpkgs need explicit typing to be useful (right now they are implicitly
any) noImplicitAny: falsein tsconfig.json: ~1k suppressed errors from circular references and variations on the fixed-point structurepkgs/by-nameentries are autogenerated — fine-grained types require actually typing the individual files
The above are known, scoped, fixable problems.
The best place to start are PRs for the above or other ergonomic fixes. PRs should be:
- Tested
- Scoped to a single fix / feature
Issues without an accompanying PR are handled on a best-effort basis.
Nix (the model) vs. Nix (the language):
- Nix (the model): the only sane way to handle system dependencies. 100% the right approach.
- Nix (the language): a bespoke runtime that served the model well, but suffers from a lack of modern tooling and is unfamiliar to most developers.
Nix (the language) is holding Nix (the model) back from widespread adoption. TypeNix is a step to unleash it.
TypeNix is the first step in unifying the best of the Nix and TypeScript ecosystems. If you are interested follow on X or Github.
Apache-2.0 (inherited from typescript-go)
