Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Stubs

TLDR: .tix files declare types for external Nix code — like TypeScript’s .d.ts files. Tix ships with built-in stubs for common nixpkgs functions, and you can generate stubs from NixOS/Home Manager option trees.

What are stubs?

Nix’s import system makes full-program inference impractical. You’re not going to infer all of nixpkgs. Stubs let you declare types for code that lives outside your project.

tix inspect my-file.nix --stubs ./my-stubs/

--stubs takes a file or directory (recursively finds .tix files). Can be passed multiple times. Built-in stubs load by default (--no-default-stubs to disable).

Writing stubs

Basic syntax

# Line comments

# Type aliases — lowercase vars are implicitly generic
type Derivation = { name: string, system: string, ... };
type Nullable = a | null;

# Value declarations
val mkDerivation :: { name: string, src: path, ... } -> Derivation;

# Modules — nest values and create type aliases from the module name
module lib {
  val id :: a -> a;
  module strings {
    val concatStringsSep :: string -> [string] -> string;
  }
}
# ^ creates type alias "Lib" = { id: a -> a, strings: { concatStringsSep: ... }, ... }

Type expressions

Same syntax as doc comment annotations — see Type Annotations.

Modules create type aliases

When you write module foo { ... }, tix auto-generates a type alias Foo (capitalized) representing the attrset type of that module’s contents. This is how Lib and Pkgs work in the built-in stubs.

Top-level val declarations

Top-level val declarations (outside any module) provide types for unresolved names automatically — no annotation needed in your Nix code:

val mkDerivation :: { name: string, ... } -> Derivation;
# No annotation needed — mkDerivation is resolved from stubs
mkDerivation { name = "my-pkg"; src = ./.; }

Built-in stubs

Tix ships with stubs for common nixpkgs functions. These are compiled into the binary and loaded by default. They cover:

  • Pkgs: mkDerivation, stdenv.mkDerivation, fetchurl, fetchFromGitHub, runCommand, writeText, etc.
  • Lib: ~500 declarations covering strings, lists, attrsets, trivial, fixedPoints, options, modules, fileset, filesystem, path, sources, versions, debug, generators, customisation, meta, asserts, gvariant, network, and more. Generated from noogle.dev data.
  • Derivation: type alias for { name: string, system: string, builder: path | string, ... }

Use --no-default-stubs if you want to replace them entirely with your own.

Built-in context stubs

When used in a tix.toml context, @-prefixed stub names refer to built-in context sources:

StubSourceProvides
@nixosCompiled-in NixOS context stubsconfig, lib, pkgs, options, modulesPath
@home-managerCompiled-in Home Manager context stubsconfig, lib, pkgs, osConfig
@callpackageDerived from Pkgs module aliasAll fields from module pkgs in the built-in stubs (stdenv, fetchurl, lib, mkDerivation, etc.)

@callpackage doesn’t require a separate stub file. It extracts the fields of the Pkgs type alias (created by module pkgs { ... } in the built-in stubs) and provides them as context args. This is the same mechanism that any module foo { ... } declaration uses: @foo resolves to Foo.

Generating stubs from NixOS/Home Manager

Tix can generate stubs from NixOS options, Home Manager options, and nixpkgs package sets. This gives you typed access to config, lib, pkgs, and other parameters in your Nix files.

From a flake

# NixOS options
tix stubs generate nixos --flake . --hostname myhost -o nixos.tix

# Home Manager options
tix stubs generate home-manager --flake . --username jr -o hm.tix

From nixpkgs directly

tix stubs generate nixos --nixpkgs /path/to/nixpkgs -o nixos.tix

Options

FlagDescription
--flake PATHFlake directory to evaluate
--hostname NAMENixOS hostname (required if multiple configurations)
--username NAMEHome Manager username (required if multiple configurations)
--nixpkgs PATHPath to nixpkgs (default: <nixpkgs> from NIX_PATH)
--from-json PATHRead pre-computed option tree JSON instead of running nix eval
-o, --output PATHOutput file (default: stdout)
--max-depth NMaximum recursion depth for option tree walking (default: 8)
--descriptionsInclude option descriptions as doc comments

Generating pkgs stubs

For callPackage-style files, you can auto-generate val declarations for all of nixpkgs:

tix stubs generate pkgs -o generated-pkgs.tix

This evaluates nixpkgs and classifies each attribute:

  • Derivations become val hello :: Derivation;
  • Non-derivation attrsets become val xorg :: { ... };
  • Functions become val callPackage :: a -> b;

Sub-package-sets like llvmPackages, python3Packages, and xorg that have recurseForDerivations = true are recursed into and emitted as nested modules:

module pkgs {
  val hello :: Derivation;
  module python313Packages {
    val numpy :: Derivation;
    val pandas :: Derivation;
  }
  val python3Packages :: Python313Packages;
  val writeText :: a -> b;
}

Alias detection: Nixpkgs uses dontRecurseIntoAttrs on alias package sets (e.g. python3Packages = dontRecurseIntoAttrs python313Packages). When a non-recursed attrset has recurseForDerivations explicitly set to false and its builtins.attrNames matches a recursed sibling, tix emits a type alias reference (val python3Packages :: Python313Packages;) instead of an opaque { ... }. This gives alias sets the same typed fields as their targets.

Use --max-depth to control recursion depth (default: 1). Higher values give more coverage but increase eval time — python3Packages alone has ~10k attributes. Use --max-depth 0 for flat output (no recursion).

The output is a module pkgs { ... } block that merges with the hand-curated module pkgs in the built-in stubs, extending the Pkgs type alias with thousands of additional fields. Since @callpackage derives its context from Pkgs, the generated packages are picked up automatically.

# Generate from specific nixpkgs
tix stubs generate pkgs --nixpkgs /path/to/nixpkgs -o generated-pkgs.tix

# Recurse deeper into sub-package-sets
tix stubs generate pkgs --max-depth 2 -o generated-pkgs.tix

# Flat output (no sub-package-set recursion, like pre-v0.x behavior)
tix stubs generate pkgs --max-depth 0 -o generated-pkgs.tix

# From pre-computed JSON (for reproducibility or CI)
tix stubs generate pkgs --from-json classified.json -o generated-pkgs.tix

Load the generated file via --stubs or the stubs config key:

stubs = ["./generated-pkgs.tix"]

[context.callpackage]
includes = ["pkgs/**/*.nix"]
stubs = ["@callpackage"]

Using generated stubs with tix.toml

Once generated, point your tix.toml at them. See Configuration.

Source annotations

Stub declarations can carry @source annotations that link back to the original source file. When present, go-to-definition in the LSP jumps directly to the nixpkgs (or home-manager) source instead of landing in the generated .tix file.

Syntax

@source <source-id>:<relative-path>:<line>:<column>

For example:

@source nixpkgs:lib/trivial.nix:61:8
val id :: a -> a;

@source can appear before val, type, and module declarations, as well as on individual attrset fields (used in NixOS/Home Manager option stubs).

How it works

When stubs are generated with --source-root nixpkgs=/nix/store/...-source, absolute Nix store paths from builtins.unsafeGetAttrPos are stripped against the root to produce relative paths. At LSP startup the source root is resolved (typically from the flake lock) so that go-to-definition can open the real file.

When using [stubs.generate] in tix.toml, source roots are passed automatically — no manual --source-root flags needed.

Using stubs in your code

Assign stub types to imports via doc comments:

let
  /** type: lib :: Lib */
  lib = import <nixpkgs/lib>;

  /** type: pkgs :: Pkgs */
  pkgs = import <nixpkgs> {};

  greeting = lib.strings.concatStringsSep ", " ["hello" "world"];
  drv = pkgs.stdenv.mkDerivation { name = "my-package"; src = ./.; };
in
{ inherit greeting drv; }

Now lib.strings.concatStringsSep is typed as string -> [string] -> string, and drv is typed as Derivation.