microsoft / microsoft/TypeScript

🚀 Proposal: Cross-Context Function Annotation (for APIs like `page.evaluate` of playwright)

Đang mở
#63,194 3 bình luận 2 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Awaiting More Feedback Suggestion
Ngôn ngữ chính
Go
Star
111k
Fork
14.3k
Merge trung bình
2 ngày 4 giờ
Pull request đã merge (30 ngày)
132

Mô tả

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:

  1. Executes in a separate context
  2. Has no access to outer lexical scope
  3. 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 TGlobal are 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)
  • postMessage semantics
  • Rust’s Send / Sync closure 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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Issue không nêu tên tệp nguồn, các bài kiểm thử hay các entry point của compiler. Hãy bắt đầu bằng cách so sánh các cách tiếp cận được đề xuất về syntax, type-level và JSDoc với các ví dụ về closure-capture và global access, sau đó xác định hành vi của compiler và các bài kiểm thử cần thiết để chứng minh sự cô lập giữa các context.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript, typescript
Lĩnh vực
compilers
Loại issue
Tính năng
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
25/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.