microsoft / microsoft/TypeScript

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

未关闭
#63,194 3 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

Awaiting More Feedback Suggestion
主要语言
Go
星标
111k
派生
14.3k
平均合并
2 天 4 小时
30 天内合并 PR
132

描述

### Acknowledgement

- [x] 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:

```ts
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

```ts
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)

```ts
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` Type-Level Mechanism

If new syntax is not desirable, introduce a type-level mechanism:

```ts
type CrossContext =
(this: TGlobal, ...args: TArgs) => TResult;
```

Library usage example:

```ts
interface Page {
evaluate(
fn: CrossContext,
arg: A
): Promise;
}
```

With compiler rule:

- Functions of type `CrossContext<...>` may not reference outer lexical bindings
- Only globals available on `TGlobal` are allowed

Example behavior:

```ts
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

```ts
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` constraint would prevent runtime errors and better reflect modern JavaScript runtime architectures.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

该 issue 没有指出源文件、测试或编译器入口点。首先,将提议的语法、type-level 和 JSDoc 方案与闭包捕获和全局访问示例进行比较,然后定义编译器行为以及证明不同上下文之间隔离性所需的测试。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, typescript
领域
compilers
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
需要澄清
新手友好度
25/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。