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

Type System

Perry erases types at compile time, similar to how tsc removes type annotations when emitting JavaScript. However, Perry also performs type inference to generate efficient native code.

Type Inference

Perry infers types from expressions without requiring annotations:

let x = 5;           // inferred as number
let s = "hello";     // inferred as string
let b = true;        // inferred as boolean
let arr = [1, 2, 3]; // inferred as number[]

Inference works through:

  • Literal values: 5number, "hi"string
  • Binary operations: a + b where both are numbers → number
  • Variable propagation: if x is number, then let y = x is number
  • Method returns: "hello".trim()string, [1,2].lengthnumber
  • Function returns: user-defined function return types are propagated to callers
function double(n: number): number {
  return n * 2;
}
let result = double(5); // inferred as number

Type Annotations

Standard TypeScript annotations work:

let name: string = "Perry";
let count: number = 0;
let items: string[] = [];

function greet(name: string): string {
  return `Hello, ${name}`;
}

interface Config {
  port: number;
  host: string;
}

Utility Types

Common TypeScript utility types are erased at compile time (they don’t affect code generation):

type Partial<T> = { [P in keyof T]?: T[P] };
type Pick<T, K> = { [P in K]: T[P] };
type Record<K, V> = { [P in K]: V };
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type ReturnType<T> = /* ... */;
type Readonly<T> = { readonly [P in keyof T]: T[P] };

These are all recognized and erased — they won’t cause compilation errors.

Generics

Generic type parameters are erased:

function identity<T>(value: T): T {
  return value;
}

class Box<T> {
  value: T;
  constructor(value: T) {
    this.value = value;
  }
}

const box = new Box<number>(42);

At runtime, all values are NaN-boxed — the generic parameter doesn’t affect code generation.

Type Checking with --type-check

For stricter type checking, Perry can integrate with Microsoft’s TypeScript checker:

perry file.ts --type-check

This resolves cross-file types, interfaces, and generics via an IPC protocol. It falls back gracefully if the type checker is not installed.

Without --type-check, Perry relies on its own inference engine, which handles common patterns but doesn’t perform full TypeScript type checking.

Union and Intersection Types

Union types are recognized syntactically but don’t affect code generation:

type StringOrNumber = string | number;

function process(value: StringOrNumber) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else {
    console.log(value + 1);
  }
}

Use typeof checks for runtime type narrowing.

Type Guards

function isString(value: any): value is string {
  return typeof value === "string";
}

if (isString(x)) {
  console.log(x.toUpperCase());
}

The value is string annotation is erased, but the typeof check works at runtime.

Next Steps