How to build, test, and document a TypR package
A TypR package is a normal R package with one extra folder. This guide covers the full development workflow: building, testing, and documenting.
Package structure
A TypR package follows standard R package conventions:
mypackage/
DESCRIPTION
NAMESPACE
R/ # generated R code (do not edit by hand)
TypR/ # your typed source code
main.ty # entry point (mandatory)
utils.ty # additional modules
man/ # documentation (auto-generated)
tests/ # testthat tests
mypackage.Rproj
The TypR/ folder is the only addition. Everything else — DESCRIPTION,
NAMESPACE, R/, man/, tests/ — is standard R.
Step 1: Build the package
Write your typed code in TypR/main.ty:
type Person <- list {
name: char,
age: int
};
let new_person <- fn(name: char, age: int): Person {
list(name = name, age = age)
};
let greet <- fn(p: Person): char {
paste("Hello,", p$name)
};
Run the build from the package root:
typr build
This transpiles every .ty file in TypR/ into R code in R/:
R/
a_std.R # generated helpers
b_generic_functions.R # generated helpers
c_types.R # generated type definitions
d_main.R # transpiled main.ty
The naming convention (a_, b_, c_, d_) ensures correct load order.
Step 2: Test with testthat
TypR has a built-in Test block that extracts into standard testthat files
during transpilation:
Test {
test_that("new_person creates a valid person", {
let p <- new_person("Alice", 25);
expect_equal(p$name, "Alice");
expect_equal(p$age, 25);
})
test_that("greet returns a greeting string", {
let p <- new_person("Bob", 30);
expect_equal(greet(p), "Hello, Bob");
})
}
Run tests as you would for any R package:
devtools::test()
# or
testthat::test_local()
Place Test blocks directly after the functions they test. This keeps
logic and tests side by side — the transpiler extracts them into
tests/testthat/ automatically.
Step 3: Document with roxygen2
TypR generates roxygen2-compatible comments from your type annotations. The
transpiler infers @param, @return, and @export directives from the
function signatures:
@pub let greet <- fn(p: Person): char {
paste("Hello,", p$name)
};
The @pub keyword generates an @export directive. After transpilation,
run:
devtools::document()
This populates man/ with .Rd files and updates NAMESPACE.
Step 4: Install and check
devtools::install()
devtools::check()
The package installs and checks like any standard R package. CRAN, R CMD check, and pkgdown all see regular R code — TypR is invisible at this stage.
Step 5: Use modules for clean organization
As your package grows, split code into one file per type or concept:
# main.ty
mod person;
mod utils;
TypR resolves mod person to TypR/person.ty and generates R/person.R.
This keeps the codebase modular without any extra configuration.
See Modules & Imports for the full module system.
Incremental migration
You do not have to convert an entire R package at once. The recommended approach:
- Add a
TypR/folder to your existing package - Move one function at a time from
R/toTypR/ - Run
typr buildto regenerate the R code - Run
devtools::test()to verify nothing broke - Repeat
TypR never forces an all-or-nothing choice.
Common build issues
| Problem | Solution |
|---|---|
main.ty not found | Ensure TypR/main.ty exists (mandatory entry point) |
Collision with R/ file | TypR overwrites R/<name>.R when TypR/<name>.ty exists. Do not edit generated files by hand. |
| Missing type errors | Run typr build — the compiler reports type errors before generating R code |
@importFrom not in NAMESPACE | Run devtools::document() after typr build |
Where to go next
- Compatibility with R — full package walkthrough
- Type existing R functions — adding
@signatures - Modules & Imports — organizing code into modules