mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter/eslint/id-length): exempt TS interface/type-literal members with never (#26310)
## Summary The `id-length` lint rule lets users opt out of checking property name lengths via a `properties: "never"` setting. This exemption worked correctly for plain JavaScript object properties, but was silently ignored for TypeScript interface members and object type literal members. As a result, users who explicitly disabled property length checks would still see false-positive warnings on their TypeScript type definitions. ## Why TypeScript interfaces and type literals describe object shapes using their own dedicated syntax, distinct from JavaScript object literals. The rule's internal logic only recognized the JavaScript form when deciding whether to skip the length check, so the TypeScript equivalents were treated as ordinary identifiers instead of property names, even though users intended the same exemption to apply to both. ## Changes - The rule now recognizes TypeScript interface and type literal members as property names, so the `properties: "never"` setting applies to them consistently with plain JavaScript object properties. - Added test coverage confirming both the exempted and non-exempted behavior for TypeScript interface and type literal members. ## Test plan - Added passing test cases showing the exemption now applies to TypeScript interface and type literal members. - Added failing test cases confirming the rule still flags these members under its default settings. - Verified the fix manually against the exact reproduction case reported in the issue, confirming the false-positive warning no longer occurs. - Full linter test suite passes with no regressions. ## AI disclosure Per the repo's AI usage policy: this fix was drafted with AI assistance (Claude), and reviewed, tested, and understood by me before submission. The linked issue itself was also AI-drafted and reviewed by me. Resolves #26309 --------- Signed-off-by: Cameron <cameron.clark@hey.com> Co-authored-by: Cameron <cameron.clark@hey.com>
This commit is contained in:
1 parent
0f4af5d7df
commit
253cdb2f46
2 files changed
+47
-13
No files matched your search
@@ -332,6 +332,8 @@ impl IdLength {
|
||||
}
|
||||
}
|
||||
AstKind::ObjectProperty(_)
|
||||
| AstKind::TSPropertySignature(_)
|
||||
| AstKind::TSMethodSignature(_)
|
||||
if self.properties == AlwaysNever::Never => {
|
||||
return;
|
||||
}
|
||||
@@ -598,19 +600,23 @@ fn test() {
|
||||
"export { default } from 'foo.json' with { type: 'json' }",
|
||||
Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
), // { "ecmaVersion": 2025 },
|
||||
// TODO:
|
||||
// (
|
||||
// "import('foo.json', { with: { type: 'json' } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 },
|
||||
// (
|
||||
// "import('foo.json', { 'with': { type: 'json' } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 },
|
||||
// (
|
||||
// "import('foo.json', { with: { type } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 }
|
||||
("interface Foo { a: string; }", Some(serde_json::json!([{ "properties": "never" }]))),
|
||||
("interface Foo { a(): void; }", Some(serde_json::json!([{ "properties": "never" }]))),
|
||||
("type Foo = { a: string };", Some(serde_json::json!([{ "properties": "never" }]))),
|
||||
("type Foo = { a(): void };", Some(serde_json::json!([{ "properties": "never" }]))),
|
||||
// TODO:
|
||||
// (
|
||||
// "import('foo.json', { with: { type: 'json' } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 },
|
||||
// (
|
||||
// "import('foo.json', { 'with': { type: 'json' } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 },
|
||||
// (
|
||||
// "import('foo.json', { with: { type } })",
|
||||
// Some(serde_json::json!([{ "min": 1, "max": 3, "properties": "always" }])),
|
||||
// ), // { "ecmaVersion": 2025 }
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
@@ -712,6 +718,10 @@ fn test() {
|
||||
("var { 𐌘 } = {};", None), // { "ecmaVersion": 6, },
|
||||
("var { prop: 𐌘} = {};", None), // { "ecmaVersion": 6, },
|
||||
("({ prop: obj.𐌘 } = {});", None), // { "ecmaVersion": 6, }
|
||||
("interface Foo { a: string; }", None),
|
||||
("interface Foo { a(): void; }", None),
|
||||
("type Foo = { a: string };", None),
|
||||
("type Foo = { a(): void };", None),
|
||||
];
|
||||
|
||||
Tester::new(IdLength::NAME, IdLength::PLUGIN, pass, fail).test_and_snapshot();
|
||||
|
||||
@@ -553,3 +553,27 @@ source: crates/oxc_linter/src/tester.rs
|
||||
1 │ ({ prop: obj.𐌘 } = {});
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ eslint(id-length): Identifier name is too short (< 2).
|
||||
╭─[id_length.tsx:1:17]
|
||||
1 │ interface Foo { a: string; }
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ eslint(id-length): Identifier name is too short (< 2).
|
||||
╭─[id_length.tsx:1:17]
|
||||
1 │ interface Foo { a(): void; }
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ eslint(id-length): Identifier name is too short (< 2).
|
||||
╭─[id_length.tsx:1:14]
|
||||
1 │ type Foo = { a: string };
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ eslint(id-length): Identifier name is too short (< 2).
|
||||
╭─[id_length.tsx:1:14]
|
||||
1 │ type Foo = { a(): void };
|
||||
· ─
|
||||
╰────
|
||||
Reference in new issue
Block a user