Skip to content

[Feature]: TS/JS: this.field.method() gets no calls edge when the field is a class field (private repo: Repo), only when it is a constructor parameter #4230

Closed

Description

@rohit-jsfreaky

Problem or use case

_resolve_typescript_member_calls types this.repo.findById() from a per-file name -> Type table. That table gets constructor parameter properties (#1316), const x = new Foo() locals and typed parameters (#1630), but never class fields. The way most NestJS, Angular and TypeORM classes hold their collaborators is a field (shapes taken from NestJS, hono and TypeORM, combined into one class):

export class Example {
  protected cookieSigner: CookieSigner | undefined;
  private readonly logger = new Logger('HttpAdapter');
  #root: Node = new Node();
  declare readonly manager: MongoEntityManager;

  handle() {
    this.cookieSigner?.sign(...);   // no edge
    this.logger.log(...);           // no edge
    this.#root.insert(...);         // no edge
    this.manager.find(...);         // no edge
  }
}

Measured with the TypeScript compiler as ground truth (checker.getSymbolAtLocation on every call inside every named function; kept when the target is a function/method/class in the repo), on 0.9.80:

repo this.x.method() calls found of the misses, field declared as a class field
NestJS (packages/, 664 files) 156 / 326 (47.9%) 140 of 164
TypeORM (packages/typeorm/src, 496 files) 360 / 558 (64.5%) 155 of 188

So affected Injector.loadPerContext and similar miss most callers in these codebases.

Proposed solution

In _ts_receiver_type_table, also record class fields (public_field_definition / field_definition):

  • name: T and name?: T -> T (same single-type_identifier rule as typed parameters);
  • name: T | undefined / T | null -> T;
  • an untyped field initialized with new T(...) -> T;
  • #private fields keyed by their #name, the receiver the extractor already records for this.#name.m();
  • static fields skipped (they are read as Cls.field).

Arrays, generics and real unions stay untyped, and the existing #2553 origin gate decides as before.

Alternatives considered

  • A full type checker (run tsc): heavy dependency, needs installed packages, not deterministic across environments.
  • Keying fields separately from locals: the table is per file and first-binding-wins today for every source; fields follow the same rule, with constructor parameter properties still winning.

Area

Extraction or language support

Are you willing to submit a PR?

Yes, I can work on this

Compatibility / behavior impact

Adds INFERRED calls edges only (0.85, as the existing table entries). No node, id or existing edge changes. JS/TS files bypass the AST cache, so no cache schema change. Changed-files rebuilds give the same edges as a full build (checked).

Additional context

PR right after this issue. 0.9.80 (6478eb7), Windows 11, Python 3.12.12, TypeScript 5.9.3 for the ground truth.

Activity

  1. github-actions commented on Oct 8, 2026

    @github-actions

    Thanks for opening this issue, @rohit-jsfreaky. A maintainer will take a look soon.

    If you would like to discuss it in real time, come say hi on our Discord server. For longer-form questions and ideas there is also GitHub Discussions.

    To help us triage, please make sure the report includes what you expected, what actually happened, and the steps (and a small sample) to reproduce it.

  2. safishamsi commented on Oct 8, 2026

    @safishamsi
    Member

    Fixed by #4231 (thanks @rohit-jsfreaky), shipped in v0.9.81. Closing as resolved.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions