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

Limitations

Perry compiles a practical subset of TypeScript. This page documents what’s not supported or works differently from Node.js/tsc.

No Runtime Type Checking

Types are erased at compile time. There is no runtime type system — Perry doesn’t generate type guards or runtime type metadata.

// These annotations are erased — no runtime effect
const x: number = someFunction(); // No runtime check that result is actually a number

Use explicit typeof checks where runtime type discrimination is needed.

No eval() or Dynamic Code

Perry compiles to native code ahead of time. Dynamic code execution is not possible:

// Not supported
eval("console.log('hi')");
new Function("return 42");

No Decorators

TypeScript decorators are not currently supported:

// Not supported
@Component
class MyClass {}

No Reflection

There is no Reflect API or runtime type metadata:

// Not supported
Reflect.getMetadata("design:type", target, key);

No Dynamic require()

Only static imports are supported:

// Supported
import { foo } from "./module";

// Not supported
const mod = require("./module");
const mod = await import("./module");

No Prototype Manipulation

Perry compiles classes to fixed structures. Dynamic prototype modification is not supported:

// Not supported
MyClass.prototype.newMethod = function() {};
Object.setPrototypeOf(obj, proto);

No Symbol Type

The Symbol primitive type is not currently supported:

// Not supported
const sym = Symbol("description");

No WeakMap/WeakRef

Weak references are not implemented:

// Not supported
const wm = new WeakMap();
const wr = new WeakRef(obj);

No Proxy

The Proxy object is not supported:

// Not supported
const proxy = new Proxy(target, handler);

Limited Error Types

Error and basic throw/catch work, but custom error subclasses have limited support:

// Works
throw new Error("message");

// Limited
class CustomError extends Error {
  code: number;
  constructor(msg: string, code: number) {
    super(msg);
    this.code = code;
  }
}

Single-Threaded Execution

User code runs on a single thread. Async I/O runs on Tokio worker threads, but there’s no SharedArrayBuffer or true multi-threading for user code.

worker_threads is supported for background tasks, but workers communicate via message passing (not shared memory).

No Computed Property Names

Dynamic property keys in object literals are limited:

// Supported
const key = "name";
obj[key] = "value";

// Not supported
const obj = { [key]: "value" };

npm Package Compatibility

Not all npm packages work with Perry:

  • Natively supported: ~50 popular packages (fastify, mysql2, redis, etc.) — these are compiled natively. See Standard Library.
  • compilePackages: Pure TS/JS packages can be compiled natively via configuration.
  • Not supported: Packages requiring native addons (.node files), eval(), dynamic require(), or Node.js internals.

Workarounds

Dynamic Behavior

For cases where you need dynamic behavior, use the JavaScript runtime fallback:

import { jsEval } from "perry/jsruntime";
// Routes specific code through QuickJS for dynamic evaluation

Type Narrowing

Since there’s no runtime type checking, use explicit checks:

// Instead of relying on type narrowing from generics
if (typeof value === "string") {
  // String path
} else if (typeof value === "number") {
  // Number path
}

Next Steps