Skip to main content

Type System Reference

Complete reference for all types in Infr.

Primitive types

These map directly to R's atomic vector types:

Infr typeR typeofExamples
numericdouble3.14, 1e10
integerinteger1L, 2L
charactercharacter"hello"
logicallogicalTRUE, FALSE
complexcomplex1+2i
rawrawas.raw(0x1f)

All of these are vectors. numeric means "a numeric vector" (possibly of length 1).

Compound types

Lists

const mixed: list <- list(1, "a", TRUE)

Named lists (record-like)

const person: list<{name: character, age: numeric}> <- list(name = "Alice", age = 30)

person$name # character
person$age # numeric
person$email # Error: Field `email` does not exist

Data frames

const df: data.frame<{
id: integer,
name: character,
score: numeric
}> <- data.frame(
id = 1:3L,
name = c("Alice", "Bob", "Charlie"),
score = c(95.5, 87.0, 92.3)
)

df$score # numeric
df$nonexistent # Error: Column `nonexistent` does not exist

Special types

TypeMeaning
anyOpts out of type checking. Escape hatch.
NULLThe R NULL value.
T?Nullable: T or NULL. Shorthand for T | NULL.

Type aliases

Define reusable type names with type:

type Score = numeric
type MaybeString = character?
type StringOrNum = character | numeric
type Person = list<{name: character, age: numeric}>

Use them anywhere a type annotation is expected:

add_scores <- function(a: Score, b: Score) -> Score { a + b }
const alice: Person <- list(name = "Alice", age = 30)

Type aliases are erased during transpilation — they produce no R output.

Literal types

String, numeric, and integer literals can be used as types, representing exact values:

type Greeting = "hello" | "goodbye"
type Dice = 1 | 2 | 3 | 4 | 5 | 6
type Status = "active" | "inactive" | numeric

Literal types are subtypes of their base types — "hello" is assignable to character, but character is not assignable to "hello".

Union types

const value: numeric | character <- if (flag) 42 else "hello"

Function types

const transformer: (numeric) -> numeric <- function(x: numeric) -> numeric { x * 2 }

Overloaded functions

Functions can have multiple signatures via .d.infr declaration files. When the same function name is declared multiple times, the signatures are merged into an overloaded type:

example.d.infr
#' @infr
as.character <- function(x: numeric) -> character
as.character <- function(x: logical) -> character
as.character <- function(x: complex) -> character

At call sites, Infr resolves the best matching overload based on argument types. If no overload matches, all available signatures are shown in the error message.

Generics

Simple type parameters for functions:

identity <- function<T>(x: T) -> T { x }
c <- function<T>(...: T) -> T

Type narrowing

Infr narrows types inside conditional branches based on type-checking functions:

const x: numeric | character <- get_value()

if (is.numeric(x)) {
x + 1 # x is numeric here
}

const y: numeric? <- maybe_get()
if (!is.null(y)) {
y * 2 # y is numeric here
}

if (inherits(obj, "data.frame")) {
nrow(obj) # obj is data.frame here
}

Supported narrowing functions:

  • is.numeric(), is.character(), is.logical(), is.integer()
  • is.null() / !is.null()
  • inherits()