Skip to content

Commit 16254f5

Browse files
Brooooooklynclaude
andcommitted
fix: recurse into nested type literals for computed property key detection
The computed property key collector only checked the top-level members of a TSTypeLiteral but did not recurse into property type annotations. This meant nested patterns like `{ nested: { [fromEmail]: string } }` would miss inner computed keys, incorrectly eliding those imports. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b6bdcd7 commit 16254f5

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

crates/oxc_angular_compiler/src/component/import_elision.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ impl<'a> ImportElisionAnalyzer<'a> {
203203
if prop_sig.computed {
204204
Self::collect_idents_from_expr(&prop_sig.key, result);
205205
}
206+
// Recurse into the property's type annotation to find
207+
// computed keys in nested type literals
208+
if let Some(type_ann) = &prop_sig.type_annotation {
209+
Self::collect_computed_keys_from_ts_type(
210+
&type_ann.type_annotation,
211+
result,
212+
);
213+
}
206214
}
207215
}
208216
}
@@ -1689,4 +1697,76 @@ class MyComponent {
16891697
"RecipientType used as computed property key in type annotation should be preserved"
16901698
);
16911699
}
1700+
1701+
#[test]
1702+
fn test_nested_type_literal_computed_key_preserved() {
1703+
// Computed key inside a nested type literal: { nested: { [fromEmail]: string } }
1704+
let source = r#"
1705+
import { Component, Input } from '@angular/core';
1706+
import { fromEmail } from './email.interface';
1707+
1708+
@Component({ selector: 'test' })
1709+
class MyComponent {
1710+
@Input() config: {
1711+
nested: {
1712+
[fromEmail]: string;
1713+
};
1714+
};
1715+
}
1716+
"#;
1717+
let type_only = analyze_source(source);
1718+
1719+
assert!(
1720+
!type_only.contains("fromEmail"),
1721+
"fromEmail in nested type literal should be preserved"
1722+
);
1723+
}
1724+
1725+
#[test]
1726+
fn test_deeply_nested_type_literal_computed_key_preserved() {
1727+
// Computed key three levels deep
1728+
let source = r#"
1729+
import { Component, Input } from '@angular/core';
1730+
import { myKey } from './keys';
1731+
1732+
@Component({ selector: 'test' })
1733+
class MyComponent {
1734+
@Input() data: {
1735+
level1: {
1736+
level2: {
1737+
[myKey]: number;
1738+
};
1739+
};
1740+
};
1741+
}
1742+
"#;
1743+
let type_only = analyze_source(source);
1744+
1745+
assert!(
1746+
!type_only.contains("myKey"),
1747+
"myKey in deeply nested type literal should be preserved"
1748+
);
1749+
}
1750+
1751+
#[test]
1752+
fn test_computed_key_in_nested_union_type_literal_preserved() {
1753+
// Computed key inside a type literal nested within a union
1754+
let source = r#"
1755+
import { Component, Input } from '@angular/core';
1756+
import { myKey } from './keys';
1757+
1758+
@Component({ selector: 'test' })
1759+
class MyComponent {
1760+
@Input() data: {
1761+
field: { [myKey]: string } | null;
1762+
};
1763+
}
1764+
"#;
1765+
let type_only = analyze_source(source);
1766+
1767+
assert!(
1768+
!type_only.contains("myKey"),
1769+
"myKey in type literal nested within union should be preserved"
1770+
);
1771+
}
16921772
}

0 commit comments

Comments
 (0)