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.
Example
# @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.
Installation
VSCode extension:
code --install-extension $(nix build github:ryanrasti/typenix#vscode-extension --print-out-paths)/typenix.vsixNeovim:
Put 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" })
Run directly:
# needs a tsconfig.json pointing at your .nix files echo '{"include": ["**/*.nix"]}' > tsconfig.json nix run github:ryanrasti/typenix -- --noEmit
How It Works
TypeNix 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
Type Annotations
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
Building
nix build .#typenix # CLI binary nix build .#vscode-extension # VS Code extension (includes binary)
Current State
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.
Contributing
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.
Why
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.
What's Next
TypeNix is the first step in unifying the best of the Nix and TypeScript ecosystems. If you are interested follow on X or Github.
License
Apache-2.0 (inherited from typescript-go)
