for-in rejects records that may be null or undefined

已关闭
#2,403 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

已经有一个关联 PR 被合并了。

  • #2406 来自 @MathurAditya724 —— 已关闭,未合并
  • #2534 来自 @ctate —— 已合并

评估

难度
4/5
预计耗时
3-5 天
新手友好度
25/100
Issue 类型
功能
描述清晰度
描述清楚
活跃度
活跃
领域
compilers

调研方向

A pull request (#2406) already proposes a fix, so pick this up from that branch rather than starting fresh. Read lowerForIn in packages/compiler/src/frontend/lowering/lower-stmts.ts and note how it dispatches on the receiver's IR kind before reaching the unsupported-receiver branch that emits SC1052; the approach is to bind the receiver once, branch on the present record tag and reuse the existing typed record walk. Reproduce with the repro.ts snippet and node packages/cli/dist/main.js coverage repro.ts --print diagnostics, and check that coverage treats the loop as a runtime fence rather than a preflight error. Done means null and undefined iterate zero times, a side-effecting receiver evaluates once, and the listed regression cases (deletion during enumeration, labels, shared cursor bindings, per-iteration closures) pass with unchanged codegen for existing typed loops.

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

描述

We are trying to compile a complete Node.js CLI and its existing JavaScript dependencies with scriptc. One remaining language gap is a loop over an object that may be missing.

JavaScript allows for-in over null or undefined: the loop simply runs zero times. scriptc can already enumerate the object itself, but rejects a parameter whose type also allows either missing value.

Reproduction

Save as repro.ts:

function keys(value: { a: number } | null | undefined): string {
  let result = "";
  for (const key in value) result += key;
  return result;
}
console.log("[" + keys(null) + "] [" + keys(undefined) + "] [" + keys({ a: 1 }) + "]");

Node executes it successfully:

$ node repro.ts
[] [] [a]

After building the scriptc workspace, run:

node packages/cli/dist/main.js coverage repro.ts --print diagnostics

The loop reports:

SC1052: for-in over '{ a: number; } | null | undefined' receivers (records, index-signature shapes, arrays, and globalThis enumerate) are not supported yet

The same refusal occurs with { a: number } | null and { a: number } | undefined. Coverage can report this as a runtime fence rather than a preflight error, so a successful frontend result alone does not mean the loop will execute.

Expected behavior

Null and undefined should perform zero iterations. A present record should use the existing record enumeration behavior. The receiver expression must be evaluated exactly once, including when it returns a nullish value.

Where the boundary occurs

lowerForIn in packages/compiler/src/frontend/lowering/lower-stmts.ts dispatches directly on the receiver's IR kind. It handles records, arrays and checked-dynamic values, but a union with one record arm and only nullish alternatives reaches the unsupported-receiver branch.

A narrow fix can bind the receiver once, branch on the present record tag, then reuse the existing typed record walk. That would preserve live deletion checks, loop binding forms, labels and per-iteration captures without introducing new runtime semantics. Tuples and other unsupported union families can retain their current fences.

Scope and regression coverage

This reproduction uses an explicitly typed parameter. It does not depend on hoisted locals or widening a JavaScript binding after assignment, the neighboring work in #1907.

Useful regression cases include null/undefined no-ops, a side-effecting receiver expression, fixed and index-signature records, deletion/addition during enumeration, shared cursor bindings, labeled break/continue, and closures capturing each iteration's key. Existing typed loops should retain the same generated code.

Proposed fix and regression tests: #2406.

Tested environment

Reproduced with a source build from scriptc’s main development branch on macOS arm64, using Node 24.22.0 and matching native compiler/runtime packages. The source build reports version 0.4.5. The original gap was also checked against later main changes before opening the linked PR.

Source revision used for the reproduction.

主要语言
TypeScript
星标
6.4k
派生
204
平均合并
7 小时 23 分钟
30 天内合并 PR
887

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

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

vercel-labs/scriptc 的其他 Issue

查看 vercel-labs/scriptc 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

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