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

Perry Constraints (Quick Reference)

Perry compiles TypeScript directly to native machine code via Rust codegen. Several JavaScript patterns that rely on V8 runtime behavior are not supported. This page lists each constraint with concrete before/after examples.

For full explanations, see the detailed constraints page.

Constraint Table

PatternUse Instead
obj[variable] dynamic key accessif/else if per key
?. optional chainingExplicit null checks
?? nullish coalescingif (x !== undefined)
/regex/.test()indexOf or char checks
{ key } ES6 shorthand{ key: key } explicit
array.map(fn) on class fieldsfor loop
for...of on arraysfor (let i = 0; i < arr.length; i++)
c >= 'a' && c <= 'z' char rangesALPHA_STR.indexOf(c) >= 0
Closures capturing this methodsModule-level functions + module-level vars
requestAnimationFramesetInterval
setTimeout self-recursionsetInterval
String-returning functions in asyncInline string operations
new Date() in asyncDate.now()

Examples

No dynamic key access

// Don't
const value = obj[key];

// Do
let value: string;
if (key === "name") {
  value = obj.name;
} else if (key === "type") {
  value = obj.type;
}

No optional chaining

// Don't
const name = user?.profile?.name;

// Do
let name: string | undefined;
if (user !== null && user !== undefined) {
  if (user.profile !== null && user.profile !== undefined) {
    name = user.profile.name;
  }
}

No nullish coalescing

// Don't
const port = config.port ?? 8080;

// Do
let port = 8080;
if (config.port !== undefined) {
  port = config.port;
}

No regex

// Don't
if (/^[a-z]+$/.test(input)) { ... }

// Do
const ALPHA = "abcdefghijklmnopqrstuvwxyz";
let allAlpha = true;
for (let i = 0; i < input.length; i++) {
  if (ALPHA.indexOf(input[i]) < 0) {
    allAlpha = false;
    break;
  }
}

No ES6 shorthand properties

// Don't
const obj = { name, age };

// Do
const obj = { name: name, age: age };

No array.map() on class fields

// Don't
class Editor {
  lines: string[] = [];
  getUpperLines() {
    return this.lines.map((l) => l.toUpperCase());
  }
}

// Do
class Editor {
  lines: string[] = [];
  getUpperLines(): string[] {
    const result: string[] = [];
    for (let i = 0; i < this.lines.length; i++) {
      result.push(this.lines[i].toUpperCase());
    }
    return result;
  }
}

No for...of on arrays

// Don't
for (const item of items) {
  process(item);
}

// Do
for (let i = 0; i < items.length; i++) {
  process(items[i]);
}

No character range comparisons

// Don't
if (c >= 'a' && c <= 'z') { ... }

// Do
const ALPHA_LOWER = "abcdefghijklmnopqrstuvwxyz";
if (ALPHA_LOWER.indexOf(c) >= 0) { ... }

No closures capturing this

// Don't
class App {
  count = 0;
  start() {
    setInterval(() => {
      this.count++;       // closure captures `this` by value
      this.render();
    }, 16);
  }
}

// Do
let count = 0;

function tick() {
  count++;
  render();
}

function render() { /* ... */ }

setInterval(tick, 16);

Closure rule: Perry captures closure variables by value, not by reference. Mutations inside a closure do not affect the outer variable. Store mutable state in module-level let variables and access them through module-level named functions.

No requestAnimationFrame

// Don't
function loop() {
  update();
  requestAnimationFrame(loop);  // never fires in Perry
}

// Do
setInterval(update, 16);

No setTimeout self-recursion

// Don't
function poll() {
  fetchData();
  setTimeout(poll, 1000);  // only fires once in Perry
}

// Do
setInterval(fetchData, 1000);

No string-returning functions in async

// Don't
async function getName(): Promise<string> {
  return buildName(first, last);  // returns NaN-boxed pointer
}

// Do
async function getName(): Promise<string> {
  const name = first + " " + last;  // inline the string operation
  return name;
}

No new Date() in async

// Don't
async function logTime() {
  const now = new Date();
  console.log(now.toISOString());
}

// Do
async function logTime() {
  const now = Date.now();
  console.log(now.toString());
}