tynix

Gradual types for Nix.

Add types where they help, keep the rest dynamic, and ship plain .nix with zero runtime.

Installercurl -fsSL https://tynix.dev/install.sh | sh
Nix flakenix profile install github:ubugeeei-prod/tynix
The tynix mark: six interlocking lambdas around an amber double colon
greet.tynixtynix
type User = { name :: String; admin :: Bool; };

let
  greet :: User -> String;
  greet = (user :: User):
    if user.admin
    then "Welcome back, ${user.name}."
    else "Hello, ${user.name}!";
in greet { name = "Ada"; admin = true; }
greet.nixnix
let
  greet = user: if user.admin
  then "Welcome back, ${user.name}."
  else "Hello, ${user.name}!";
in greet {
  name = "Ada";
  admin = true;
}

Amber is type-only syntax: tynix checks it, then erases it. Misspell user.nmae and you get [TC0009] missing field `nmae` before Nix evaluates anything.

How it works

One binary checks your files and compiles them back to Nix. Your flake, your builds and nix eval only ever see .nix.

Write

Annotate a .tynix file, or describe a .nix file you keep with a .d.tynix declaration.

shell
tynix init

Check

Type-check one file or the whole project. Every diagnostic has a stable code and a precise location.

shell
tynix check-project

Ship

Erase every type and emit plain .nix next to the declarations other projects can import.

shell
tynix build
  • VS Code
  • Cursor
  • VSCodium
  • Zed
  • Neovim
  • Helix

Run tynix ide install to set up the language server in any of them. See Editors for what each one gets.

Ready in fifteen minutes

Install tynix, type your first file, and finish with a checked flake.