microsoft / microsoft/TypeScript
🚀 Proposal: Cross-Context Function Annotation (for APIs like `page.evaluate` of playwright)
Nessuno ha ancora preso questa issue.
- Lingua principale
- Go
- Stelle
- 111k
- Fork
- 14.3k
- Merge medio
- 2g 4h
- PR unite (30g)
- 132
Descrizione
Acknowledgement
- I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.
Comment
Motivation
APIs such as page.evaluate in browser automation libraries (e.g. Playwright) execute a function in a different JavaScript runtime context (browser context instead of Node.js).
Example:
const myText = "Hello world";
await page.evaluate(() => {
console.log(myText); // ❌ Runtime error (myText is not defined in browser)
});
Although this fails at runtime, TypeScript does not report an error. The compiler assumes the lambda executes in the same lexical and global environment as the caller.
This creates a mismatch between TypeScript’s static model and the actual runtime semantics of cross-context APIs.
Problem Statement
TypeScript currently has no way to express that:
- A function executes in a separate runtime context
- The function must not capture outer-scope variables
- The function must not access Node.js globals
- The function runs against a specific global type (e.g.
Window)
As a result:
- Accidental closure capture is allowed
- Incorrect global usage is not flagged
- Runtime errors occur that could be prevented at compile time
Real-World Example
const secret = 42;
await page.evaluate(() => {
console.log(secret); // ❌ Runtime error: secret is not defined
});
TypeScript currently allows this because it has no notion of execution context isolation.
Proposed Solution
Introduce a way to declare that a function:
- Executes in a separate context
- Has no access to outer lexical scope
- Uses a specified global type
Option A: isolated Function Modifier (New Syntax)
const myText = "Hello world";
await page.evaluate(isolated () => {
console.log(document.title); // ✅ OK
console.log(myText); // ❌ Compile error
});
Semantics:
- No closure capture allowed
- No outer-scope variable access
- Global type defined by API signature
- Treated as if compiled in a separate program with structured-clone semantics
This would be conceptually similar to --isolatedModules, but at function scope.
Option B: CrossContext<TGlobal> Type-Level Mechanism
If new syntax is not desirable, introduce a type-level mechanism:
type CrossContext<TGlobal, TArgs extends any[], TResult> =
(this: TGlobal, ...args: TArgs) => TResult;
Library usage example:
interface Page {
evaluate<R, A>(
fn: CrossContext<Window, [A], R>,
arg: A
): Promise<R>;
}
With compiler rule:
- Functions of type
CrossContext<...>may not reference outer lexical bindings - Only globals available on
TGlobalare allowed
Example behavior:
const x = 5;
await page.evaluate(() => {
console.log(x); // ❌ Error: outer scope capture not allowed
console.log(process); // ❌ Error: not part of Window
console.log(document); // ✅ OK
});
Option C: JSDoc Annotation
await page.evaluate(
/** @crossContext Window */
() => {
console.log(document.title);
}
);
Compiler behavior:
- Treat function as isolated
- Restrict global access
- Disallow closure capture
This avoids syntax changes while still enabling static safety.
Prior Art
- Web Workers (structured cloning, no shared closures)
postMessagesemantics- Rust’s
Send/Syncclosure restrictions - Sandboxed runtimes (e.g. Electron multi-process model)
Benefits
- Prevents a common class of runtime errors
- Improves safety of browser automation, workers, and SSR
- Makes execution semantics explicit
- Improves editor tooling and developer experience
- Enables safer multi-runtime APIs
Non-Goals
- Not intended as a security boundary
- Not intended to restrict normal functions
- Not a replacement for ESLint, but a stronger compiler guarantee
Why This Belongs in TypeScript (Not Just ESLint)
While ESLint could detect some closure capture cases, only the TypeScript compiler can:
- Properly reason about type environments
- Enforce global type restrictions
- Integrate deeply with existing type inference
Cross-context execution is increasingly common in:
- Browser automation
- Workers
- Hybrid runtimes
- SSR frameworks
TypeScript currently has no way to model this distinction.
Summary
TypeScript lacks a mechanism to model execution-context isolation. Introducing an isolated function modifier or a CrossContext<TGlobal> constraint would prevent runtime errors and better reflect modern JavaScript runtime architectures.
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
L’issue non indica alcun file sorgente, test o punto di ingresso del compilatore. Inizia confrontando gli approcci proposti di sintassi, type-level e JSDoc con gli esempi di cattura delle closure e accesso globale, quindi definisci il comportamento del compilatore e i test necessari per dimostrare l’isolamento tra i contesti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- javascript, typescript
- Ambito
- compilers
- Tipo di issue
- Funzionalità
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Ferma
- Chiarezza
- Da chiarire
- Idoneità per principianti
- 25/100