Skip to content

bun check: widen the type of a property without an annotation, as tsc does - #46949

Open
ebidel wants to merge 1 commit into
oven-sh:mainfrom
ebidel:check-widen-property-without-annotation
Open

ebidel wants to merge 1 commit into
oven-sh:mainfrom
ebidel:check-widen-property-without-annotation

Conversation

@ebidel

@ebidel ebidel commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

A class property without a type annotation gets its type from its initializer. With noImplicitAny it can also get its type from the assignments in the constructor or a static block, or from the base class when it's declared. tsc passes each of these types through widenTypeForVariableLikeDeclaration. bun check did that only for the initializer, which caused several differences:

  • An object literal assigned in the constructor stayed fresh, so a later use checked its excess properties: a false TS2353.
  • TS7018 and TS7008 were missing without strictNullChecks.
  • A unique symbol that tsc widens to symbol was kept, so TS2322 and TS2416 were missing.
interface Opt { label: string }
declare function take(opt: Opt): void;
class A {
  private opt;
  constructor() { this.opt = { label: "x", extra: 1 }; } // bun check 1.4.4: TS2353, 'extra' does not exist in type 'Opt'. tsc: no error.
  run() { take(this.opt); }
}

One helper, widen_type_of_property_declaration, now widens all four. As in tsc, it keeps the unique symbol of an optional property under strictNullChecks, because by then the type is a union with undefined. That also fixes x? = u, which is wrong on main.

How did you verify your code works?

  • Four new programs at the end of test/cli/check/differential-cases.json match tsc 7.0.2 byte for byte. Bun 1.4.4 differs on three of them. The fourth, with exactOptionalPropertyTypes, guards the optional case.
  • TypeScript's tests with typescript-go's baselines (bundle.zst, every test) stay at 100.00% byte for byte: 13,101 errors, 12,463 types, 12,463 symbols, 13,032 declarations and 151 traces. The 2,418 existing differential programs still match tsc 7.0.2.
  • I compared 265 more programs with tsc 7.0.2. They cover optional, readonly, #private, accessor and declare properties, merges, cycles and checkJs. None got worse, and 181 that differed on main now match. The 24 that still differ also differ on main: cycles, SymbolConstructor and parenthesized assignment targets.
  • On a large private codebase (59,366 files), this was one of six errors that bun check -b reports and tsc doesn't.
  • I didn't run bun bd test. I ran these through bun-sema from cargo build -p bun_sema_standalone, built from main at 179664e.
  • The change applies to bun check: TypeScript 7.1 #46383 as it is. There, the four programs match tsc 7.1 Beta, and 7.1's tests and differential programs still all pass.

🤖 Generated with Claude Code

… does

A property without a type annotation gets its type from its initializer.
With noImplicitAny it can also get its type from the assignments in the
constructor or a static block, or, when it's `declare`d, from the base
class. tsc passes each of these types through
`widenTypeForVariableLikeDeclaration`. bun did so only for the
initializer, which caused several differences:

- An object literal assigned in the constructor stayed fresh, so a later
  use checked its excess properties (a false TS2353).
- TS7018 and TS7008 were missing without strictNullChecks.
- A `unique symbol` that tsc widens to `symbol` was kept, so TS2322 and
  TS2416 were missing.

One helper now widens all four. As in tsc, it keeps the `unique symbol`
of an optional property under strictNullChecks, because by then the
type is a union with `undefined`. That also fixes `x? = u`, which is
wrong on main.
@ebidel
ebidel marked this pull request as ready for review October 26, 2026 03:49

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: oven-sh/bun
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 449877f
  • Layers: Repository YAML
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: f58c1184-09d5-4ca4-9759-2fb532d433ae
Commits

Reviewing files that changed from the base of the PR and between 449877f and 9beac12.

Files selected for processing: 2
  • src/sema/check/shape.rs
  • test/cli/check/differential-cases.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Walkthrough

Property declarations inferred from initializers, constructor or static-block flow, and inheritance now use shared type widening. The helper applies the optional-property unique symbol rule and conditionally reports implicit any. Four differential cases cover these paths and related compiler options.

Changes

Property type widening

Layer / File(s) Summary
Shared property widening and coverage
src/sema/check/shape.rs, test/cli/check/differential-cases.json
A shared helper widens inferred property types and conditionally reports implicit any. Initializer, constructor-flow, static-block-flow, and inherited property types use the helper. Differential cases cover constructor and static-block assignments, unique symbol inference, and optional properties.

Possibly related PRs

  • oven-sh/bun#46678: Both changes affect TypeScript type widening, though this PR addresses property declarations and that PR addresses synthesized object-literal types.

Suggested reviewers: jarred-sumner

Priority: Normal

Merge Risk: Minimal · up to 9beac

No actionable issue is established; the change is mergeable after normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The description includes both required sections. It clearly explains the property-widening changes and provides detailed verification results, including the tests not run.
Title check Passed The title is concise, specific, and accurately summarizes the main change: matching TypeScript's property type widening behavior in bun check.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant