From 1bc6d0ad6b6d660be861ee0774990541f9c00ef8 Mon Sep 17 00:00:00 2001 From: sunjiajie03 Date: Tue, 26 May 2026 15:27:27 +0800 Subject: [PATCH 1/2] fix(extraction): extract type refs from interface property_signature type annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Types that appear only in interface property signatures (e.g. `value?: Partial`) were not producing UnresolvedReferences, so the resolver never built `references` edges for them. Impact analysis consequently missed files that import-type only these interface-property types. Add a `property_signature` branch in `visitNode`: when inside a class-like node (includes interfaces) and the language supports type annotations, call `extractTypeAnnotations` with the parent (interface) node ID as the edge source. No property node is created — only unresolved references. Co-Authored-By: Claude Sonnet 4.6 --- __tests__/extraction.test.ts | 16 ++++++++++++++++ src/extraction/tree-sitter.ts | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index 92512e2b5..deeedd8c5 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -205,6 +205,22 @@ export interface User { }); }); + it('should extract type references from interface property signatures', () => { + const code = ` +import type { IPage } from '../PromoterList'; +import type { IOrderField } from '../types'; + +interface Hprops { + value?: Partial & Partial; +} +`; + const result = extractFromSource('HeaderFilter.ts', code); + + const refs = result.unresolvedReferences.filter((r) => r.referenceKind === 'references'); + expect(refs.some((r) => r.referenceName === 'IPage')).toBe(true); + expect(refs.some((r) => r.referenceName === 'IOrderField')).toBe(true); + }); + it('should track function calls', () => { const code = ` function main() { diff --git a/src/extraction/tree-sitter.ts b/src/extraction/tree-sitter.ts index d539a11e5..af0c1e784 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -386,6 +386,21 @@ export class TreeSitterExtractor { else if (nodeType === 'impl_item') { this.extractRustImplItem(node); } + // TypeScript interface property_signature: extract type references from the + // type annotation (e.g. `value?: Partial`) so the resolver can build + // a `references` edge from the interface to the named type. + // No property node is created — only unresolved references. + else if ( + nodeType === 'property_signature' && + this.isInsideClassLikeNode() && + this.TYPE_ANNOTATION_LANGUAGES.has(this.language) + ) { + const parentId = this.nodeStack[this.nodeStack.length - 1]; + if (parentId) { + this.extractTypeAnnotations(node, parentId); + } + // don't skipChildren — nested signatures still need traversal + } // Visit children (unless the extract method already visited them) if (!skipChildren) { From c08759fb720ca39f6a939462f5013f5357cd423d Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Tue, 26 May 2026 17:01:15 -0500 Subject: [PATCH 2/2] fix(extraction): also cover method_signature; add CHANGELOG entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit method_signature inside interfaces (e.g. `foo(arg: T): R`) had the same root cause as the property_signature case fixed here — interface-body type annotations were dropped because the walker matched neither propertyTypes nor fieldTypes, so extractTypeRefsFromSubtree was never called for those members. Extend the dispatch to both node types and add a matching test. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 10 ++++++++++ __tests__/extraction.test.ts | 16 ++++++++++++++++ src/extraction/tree-sitter.ts | 11 ++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94864885..be64bbda9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -170,6 +170,16 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). `docs/design/mixed-ios-and-react-native-bridging.md`. ### Fixed +- **TypeScript interface members now produce `references` edges to their + annotated types (#432).** Types that appeared only in an interface's + property or method signatures — e.g. `value?: Partial` or + `fetchPage(arg: IPage): IOrderField` — were being silently dropped at + extraction time, so `codegraph_impact`/`codegraph_callers` on the named + type missed every consumer that imported it just to use it in an + interface shape. The walker now extracts type annotations from both + `property_signature` and `method_signature` nodes inside class-like + parents (interfaces, classes), and the resolver wires the resulting + references the same way it wires field and parameter types elsewhere. - **Git worktrees no longer silently borrow another tree's index (#155).** When a worktree is nested inside the main checkout — exactly what agent tools that place worktrees under gitignored paths like diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index deeedd8c5..96b2686a3 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -221,6 +221,22 @@ interface Hprops { expect(refs.some((r) => r.referenceName === 'IOrderField')).toBe(true); }); + it('should extract type references from interface method signatures', () => { + const code = ` +import type { IPage } from '../PromoterList'; +import type { IOrderField } from '../types'; + +interface MethodForm { + fetchPage(arg: IPage): IOrderField; +} +`; + const result = extractFromSource('MethodForm.ts', code); + + const refs = result.unresolvedReferences.filter((r) => r.referenceKind === 'references'); + expect(refs.some((r) => r.referenceName === 'IPage')).toBe(true); + expect(refs.some((r) => r.referenceName === 'IOrderField')).toBe(true); + }); + it('should track function calls', () => { const code = ` function main() { diff --git a/src/extraction/tree-sitter.ts b/src/extraction/tree-sitter.ts index af0c1e784..1b3534504 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -386,12 +386,13 @@ export class TreeSitterExtractor { else if (nodeType === 'impl_item') { this.extractRustImplItem(node); } - // TypeScript interface property_signature: extract type references from the - // type annotation (e.g. `value?: Partial`) so the resolver can build - // a `references` edge from the interface to the named type. - // No property node is created — only unresolved references. + // TypeScript interface members: property_signature (`foo: T`, `foo?: T`) + // and method_signature (`foo(arg: A): R`) both carry type annotations the + // interface walker would otherwise drop. Extract them as `references` + // edges from the interface so resolvers can wire callers/impact for + // types that only appear in interface members. else if ( - nodeType === 'property_signature' && + (nodeType === 'property_signature' || nodeType === 'method_signature') && this.isInsideClassLikeNode() && this.TYPE_ANNOTATION_LANGUAGES.has(this.language) ) {