Greptile SummaryThis PR credits plain imports of erased TypeScript exports in dead-export analysis. The main changes are:
Confidence Score: 4/5The changed import-credit flow needs an extension-parity fix before merging.
src/shared/kinds.ts and crates/codegraph-core/src/shared/constants.rs Important Files Changed
Reviews (1): Last reviewed commit: "fix(exports): credit plain imports of Ty..." | Re-trigger Greptile |
Sorry, something went wrong.
| * instead of fixing a false positive. | ||
| */ | ||
| export function isTypeErasedImportTarget(kind: string, file: string): boolean { | ||
| return TYPE_ERASED_SYMBOL_KINDS.has(kind) && TYPESCRIPT_EXTENSIONS.has(path.extname(file)); |
There was a problem hiding this comment.
TypeScript Modules Stay Uncredited
When a plain import resolves to an interface or type alias in a .mts or .cts file, this check returns false because path.extname(file) is compared against a set containing only .ts and .tsx. Those TypeScript module files can still export erased declarations, so the new imports-type credit is skipped and codegraph exports can still report the used type export as dead.
Sorry, something went wrong.
|
|
||
| /// TypeScript source extensions — type annotations (and TS's compile-time-only | ||
| /// 'interface'/'type' declarations) only exist for these. | ||
| pub const TYPESCRIPT_EXTENSIONS: [&str; 2] = [".ts", ".tsx"]; |
There was a problem hiding this comment.
Native TypeScript Modules Stay Uncredited
The native builders use this list to decide whether a plain import can credit an erased TypeScript target, but it excludes .mts and .cts. A plain import of an interface or type from those module files therefore fails is_type_erased_import_target, so the native codegraph exports path can still mark the used export as dead.
| pub const TYPESCRIPT_EXTENSIONS: [&str; 2] = [".ts", ".tsx"]; | |
| pub const TYPESCRIPT_EXTENSIONS: [&str; 4] = [".ts", ".tsx", ".mts", ".cts"]; |
Sorry, something went wrong.
Codegraph Impact Analysis14 functions changed → 25 callers affected across 8 files
|
Sorry, something went wrong.
Summary
codegraph exports/dead-export analysis (the same query the pre-commit dead-export gate uses) only credited a symbol-level imports-type edge when the importing statement was syntactically marked type-only — a whole-statement import type { X } (#1724) or the inline import { type X } modifier (#1813).
TypeScript also allows importing an interface or type alias through a plain import { X } from 'y' with no type keyword at all — an extremely common pattern in codebases that don't enforce import/consistent-type-imports. Since interfaces and type aliases are erased before runtime, they can never receive a calls edge either, so a plain import was the only possible consumption signal, and it was being ignored — permanently misclassifying every such interface/type alias as a dead export, even when it's genuinely used everywhere.
Root cause & fix
imports-type crediting was gated on the importing statement's syntax rather than the resolved target's actual kind. The fix classifies crediting by whether the resolved target is a TypeScript interface/type-alias declaration (isTypeErasedImportTarget in shared/kinds.ts, mirrored as is_type_erased_import_target in Rust's shared/constants.rs) — regardless of whether the import statement used the type keyword.
Dual-engine parity
Mirrored in all four import-edge-building code paths per this repo's dual-engine architecture:
Test plan
Fixes #1833