Skip to content

Type checker: a class EXPRESSION method's inferred return type doesn't reach call sites (class declarations do) #793

@nickna

Description

@nickna

Summary

An inferred (un-annotated) method return type on a class expression (const C = class { … }) is not propagated to call sites, so new C().method() is typed too loosely. The same code on a class declaration type-checks fine — class declarations got inferred-method-return propagation via #661/#658, but class expressions were not included.

Repro

// Class DECLARATION — OK
class Range {
  constructor(public start: number, public end: number) {}
  *gen() { for (let i = this.start; i < this.end; i++) yield i; }   // inferred Generator<number>
}
let sum = 0;
for (const n of new Range(1, 5).gen()) sum += n;   // n: number → OK, prints 10
console.log(sum);
// Class EXPRESSION — fails type-check
const Range = class {
  constructor(public start: number, public end: number) {}
  *gen() { for (let i = this.start; i < this.end; i++) yield i; }
};
let sum = 0;
for (const n of new Range(1, 5).gen()) sum += n;   // Type Error: Compound assignment requires numeric operands
console.log(sum);

The failure is in the type checker (VisitCompoundAssign), in both back ends — new Range(...).gen()'s element type comes back non-numeric because the method's inferred Generator<number> return isn't visible at the call site. Annotating the method (*gen(): Generator<number>) works around it.

Scope

Type-checker only (codegen is unaffected — compiled class-expression generator methods run correctly once annotated, per #765). Likely generalizes to any inferred method return on a class expression, not just generators. Discovered while adding #765 tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions