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
| Pattern | Use Instead |
|---|---|
obj[variable] dynamic key access | if/else if per key |
?. optional chaining | Explicit null checks |
?? nullish coalescing | if (x !== undefined) |
/regex/.test() | indexOf or char checks |
{ key } ES6 shorthand | { key: key } explicit |
array.map(fn) on class fields | for loop |
for...of on arrays | for (let i = 0; i < arr.length; i++) |
c >= 'a' && c <= 'z' char ranges | ALPHA_STR.indexOf(c) >= 0 |
Closures capturing this methods | Module-level functions + module-level vars |
requestAnimationFrame | setInterval |
setTimeout self-recursion | setInterval |
| String-returning functions in async | Inline string operations |
new Date() in async | Date.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
letvariables 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());
}