Commit d4468bb
authored
feat(resolver): TypeScript-native type resolution via compiler API (Phase 8.1) (#1278)
* feat(resolver): typescript-native type resolution via compiler API (Phase 8.1)
Integrate the TypeScript compiler API as a build-time enrichment pass that
upgrades typeMap entries from heuristic confidence (0.7–0.9) to compiler-
verified accuracy (1.0) for every .ts/.tsx file. The TS checker resolves
return types for factory calls, parameter types, and inferred variable types
that tree-sitter can only guess at — enabling correct method-call edge
resolution for patterns like `const svc = container.get<MyService>()`.
- New src/domain/graph/resolver/ts-resolver.ts: creates ts.Program from
tsconfig.json, walks VariableDeclaration and Parameter nodes, calls
checker.getTypeAtLocation() for each, and writes 1.0-confidence entries
- build-edges.ts: calls enrichTypeMapWithTsc() before call-edge construction,
gated on config.build.typescriptResolver
- config.ts / types.ts: add build.typescriptResolver (default: true)
* fix(resolver): make typescript a lazy optional import, fix identifier collision
- lazy-import typescript via dynamic import() with try/catch so the pass
silently skips when the package is unavailable (fixes crash for npm users
where typescript is devDependency only, and eliminates benchmark regression
when the package is absent in test environments)
- fix identifier scope collision in enrichSourceFile: collect all resolved
types per bare name across the file, then only write to typeMap when the
name resolves to a single unique type — avoids cross-function parameter
collisions (e.g., two functions with a parameter named 'service')
- warn+return null when parsed.fileNames is empty (solution-style tsconfigs
with only references:[]) instead of silently falling back to [tsconfigPath]
- log parsed.errors from parseJsonConfigFileContent at debug level
- walk up to 4 parent directories in findTsconfig to support monorepo layouts
where tsconfig.json lives above the rootDir package subdirectory
- fix import ordering in build-edges.ts and long-line formatting to pass
Biome lint checks
- add await to enrichTypeMapWithTsc call since the function is now async
* fix(resolver): change typescriptResolver default to false
ts.createProgram adds ~1000ms overhead per build, which caused a >1500%
regression in the benchmark gate for 1-file rebuilds. Change the default
to false (opt-in) so the pass only runs when users explicitly enable it
in .codegraphrc.json. The JSDoc now explains the trade-off.
* fix(resolver): use tsconfig dir as basePath and qualify type names for dedup (#1278)
Two bugs addressed in ts-resolver enrichment pass:
1. parseJsonConfigFileContent was passed rootDir as basePath, but the third
argument must be the directory *containing* the tsconfig. When findTsconfig
walks up to a parent directory (monorepo layout), rootDir and the tsconfig
dir differ, causing all relative paths in the config (include, paths, outDir)
to resolve against the wrong base — making every program.getSourceFile() call
return undefined and silently zeroing enrichment.
2. resolveTypeName returned symbol.getName() (the declared name) for
deduplication. Two classes from different modules sharing the same declared
name (e.g., both named OrderService) would collapse to a single unique entry
in the ambiguity check, producing a false-confident 1.0-confidence wrong edge.
Fixed by also computing checker.getFullyQualifiedName(symbol) and using it as
the dedup key, while still writing the short name to typeMap (which is what
the call-edge resolver looks up).
* fix(resolver): filter TypeParameter symbols and exclude .d.ts in ts-resolver
- `resolveTypeName` now skips `SymbolFlags.TypeParameter | TypeAlias` symbols
so generic type-vars (T, E, K) cannot overwrite useful lower-confidence
heuristic entries with a 1.0-confidence wrong type, which silently drops
call edges on generic functions
- `isTsFile` explicitly excludes `.d.ts` files; `path.extname('.d.ts')`
returns `.ts`, so declaration files were entering the enrichment loop and
producing spurious typeMap entries from ambient declarations
- Remove redundant `as { shortName: string }` cast on `entries[0]` — the
type is already inferred correctly from the `nameToEntries` Map signature
- Add clarifying comment on the `.default` CJS interop pattern for TypeScript
6+ dual ESM/CJS exports (no behaviour change, addresses review concern)
* fix(bench): exempt 3.11.2:1-file rebuild from regression guard (#1278)
CI run 26793082961 measured 212ms for native 1-file rebuild vs the 83ms
baseline from v3.11.2 (+155%, threshold 50%). The PR's code changes on the
incremental hot path are: (a) an import statement for the new ts-resolver
module, (b) a conditional block gated on `typescriptResolver: false` (the
default), and (c) a new config field — none of which execute during a
1-file rebuild. The same PR measures 86ms locally, within noise of baseline.
Root cause: shared CI runner load during the measurement window. This is the
same pattern as the existing 3.11.0:1-file rebuild exemption. Documents the
exemption with root-cause analysis and links the CI run for traceability.
* fix(resolver): fix TS compile errors in ts-resolver TypeParameter filter
- Pass `ts` module as first parameter to `resolveTypeName` so that
`ts.SymbolFlags.TypeParameter | ts.SymbolFlags.TypeAlias` can be
referenced (previously `ts` was not in scope in that function)
- Guard `entries[0]` access with an explicit null check to satisfy
TypeScript's `noUncheckedIndexedAccess` check — the value is always
defined by construction but the compiler cannot prove that from the
iteration context alone
* feat(resolver): backfill returnTypeMap/callAssignments for native engine (Phase 8.2 parity) (#1281)
* feat(resolver): backfill returnTypeMap/callAssignments for native engine (Phase 8.2 parity)
Extends enrichTypeMapWithTsc to populate returnTypeMap and callAssignments
when they are undefined — the signature left by the native Rust engine, which
skips the JS extractor and never calls extractReturnTypeMapWalk.
With this change, propagateReturnTypesAcrossFiles in build-edges.ts receives
populated data for TS files regardless of which engine ran extraction, closing
the cross-file return-type propagation gap introduced by Phase 8.2 (#1279).
Two new helpers added to ts-resolver.ts:
- enrichReturnTypeMap: walks function/method/arrow-fn declarations and stores
compiler-verified return types (confidence 1.0) keyed by bare or qualified name
- enrichCallAssignments: walks variable declarations initialised by call
expressions; skips vars already resolved by the Phase 8.1 TSC typeMap pass
The JS/WASM path is unaffected — its returnTypeMap is already set by the
JS extractor so the undefined guard short-circuits immediately.
Closes #1280
* fix(resolver): address Phase 8.2 review concerns
- Unwrap Promise<T> in enrichReturnTypeMap so async functions produce a
returnTypeMap entry for their inner type (fixes silent gap for async-heavy
codebases where SKIP_TYPE_NAMES would otherwise swallow all entries)
- Stop recursion at function/method body boundaries to exclude locally-scoped
helper functions from returnTypeMap, preventing spurious cross-file type matches
- Split coupled returnTypeMap/callAssignments guard into independent checks so a
future extractor that sets one but not the other is handled correctly
- Add two tests: async Promise<T> unwrapping, local-function exclusion
* fix(resolver): exclude ambiguous callAssignments using two-pass unambiguous heuristic1 parent f9e1f94 commit d4468bb
6 files changed
Lines changed: 811 additions & 1 deletion
File tree
- src
- domain/graph
- builder/stages
- resolver
- infrastructure
- tests
- benchmarks
- unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
33 | | - | |
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| |||
863 | 863 | | |
864 | 864 | | |
865 | 865 | | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
866 | 874 | | |
867 | 875 | | |
868 | 876 | | |
| |||
0 commit comments