FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(linter/eslint/prefer-destructuring): handle typed declarations (#… · oxc-project/oxc@8694167 · GitHub

Commit 8694167

Browse files
committed
fix(linter/eslint/prefer-destructuring): handle typed declarations (#24616)
## Summary - skip prefer-destructuring diagnostics for variable declarations with type annotations by default - add enforceForDeclarationWithTypeAnnotation for typescript-eslint compatibility - report without an autofix when annotated declarations are explicitly enforced, preserving the declared type - cover object, computed-property, array, renamed-property, and reporter configuration cases This matches the current `@typescript-eslint/prefer-`destructuring behavior. Fixes #24615.
1 parent 94f99b3 commit 8694167

4 files changed

Lines changed: 172 additions & 49 deletions

File tree

‎apps/oxlint/src-js/package/config.generated.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,7 +1268,7 @@ export interface DummyRuleMap {
12681268
"prefer-destructuring"?:
12691269
| RuleNoConfig
12701270
| [AllowWarnDeny, PreferDestructuringOption]
1271-
| [AllowWarnDeny, PreferDestructuringOption, PreferDestructuringRenamedPropertiesConfig];
1271+
| [AllowWarnDeny, PreferDestructuringOption, PreferDestructuringEnforcementConfig];
12721272
"prefer-exponentiation-operator"?: RuleNoConfig;
12731273
"prefer-named-capture-group"?: RuleNoConfig;
12741274
"prefer-numeric-literals"?: RuleNoConfig;
@@ -4148,7 +4148,8 @@ export interface PreferDestructuringAssignmentConfig {
41484148
AssignmentExpression?: PreferDestructuringTargetOption;
41494149
VariableDeclarator?: PreferDestructuringTargetOption;
41504150
}
4151-
export interface PreferDestructuringRenamedPropertiesConfig {
4151+
export interface PreferDestructuringEnforcementConfig {
4152+
enforceForDeclarationWithTypeAnnotation?: boolean;
41524153
enforceForRenamedProperties?: boolean;
41534154
}
41544155
export interface PreferPromiseRejectErrors {

‎crates/oxc_linter/src/rules/eslint/prefer_destructuring.rs‎

Lines changed: 118 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,14 @@ impl PreferDestructuringOption {
116116

117117
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
118118
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
119-
struct PreferDestructuringRenamedPropertiesConfig {
119+
struct PreferDestructuringEnforcementConfig {
120120
enforce_for_renamed_properties: bool,
121+
enforce_for_declaration_with_type_annotation: bool,
121122
}
122123

123124
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
124125
#[serde(default)]
125-
struct PreferDestructuringConfig(
126-
PreferDestructuringOption,
127-
PreferDestructuringRenamedPropertiesConfig,
128-
);
126+
struct PreferDestructuringConfig(PreferDestructuringOption, PreferDestructuringEnforcementConfig);
129127

130128
impl PreferDestructuringConfig {
131129
fn into_rule(self) -> PreferDestructuring {
@@ -135,6 +133,9 @@ impl PreferDestructuringConfig {
135133
variable_declarator,
136134
assignment_expression,
137135
enforce_for_renamed_properties: self.1.enforce_for_renamed_properties,
136+
enforce_for_declaration_with_type_annotation: self
137+
.1
138+
.enforce_for_declaration_with_type_annotation,
138139
}
139140
}
140141
}
@@ -150,6 +151,8 @@ pub struct PreferDestructuring {
150151
assignment_expression: PreferDestructuringTargetConfig,
151152
/// Determines whether the object destructuring rule applies to renamed variables.
152153
enforce_for_renamed_properties: bool,
154+
/// Determines whether the rule applies to variable declarations with type annotations.
155+
enforce_for_declaration_with_type_annotation: bool,
153156
}
154157

155158
declare_oxc_lint!(
@@ -245,6 +248,11 @@ impl Rule for PreferDestructuring {
245248
}
246249
}
247250
AstKind::VariableDeclarator(declarator) => {
251+
let has_type_annotation = declarator.type_annotation.is_some();
252+
if has_type_annotation && !self.enforce_for_declaration_with_type_annotation {
253+
return;
254+
}
255+
248256
// Skip `using` and `await using` declarations - destructuring doesn't apply to them
249257
if matches!(
250258
declarator.kind,
@@ -272,54 +280,57 @@ impl Rule for PreferDestructuring {
272280
if self.variable_declarator.array {
273281
ctx.diagnostic(prefer_array_destructuring(init.span()));
274282
}
275-
} else {
276-
if self.enforce_for_renamed_properties
277-
&& self.variable_declarator.object
278-
{
279-
ctx.diagnostic(prefer_object_destructuring(right.span()));
280-
}
283+
} else if self.variable_declarator.object {
281284
if let Expression::StringLiteral(string_literal) =
282285
&comp_expr.expression
283-
&& self.variable_declarator.object
284286
&& name.is_some_and(|v| v == string_literal.value)
285287
{
288+
if has_type_annotation {
289+
ctx.diagnostic(prefer_object_destructuring(init.span()));
290+
} else {
291+
ctx.diagnostic_with_fix(
292+
prefer_object_destructuring(init.span()),
293+
|fixer| {
294+
generate_fix(
295+
&fixer,
296+
string_literal.span.shrink(1),
297+
get_object_span_without_redundant_parentheses(
298+
&comp_expr.object,
299+
),
300+
declarator.span(),
301+
)
302+
},
303+
);
304+
}
305+
} else if self.enforce_for_renamed_properties {
306+
ctx.diagnostic(prefer_object_destructuring(right.span()));
307+
}
308+
}
309+
}
310+
MemberExpression::StaticMemberExpression(static_expr)
311+
if self.variable_declarator.object =>
312+
{
313+
if name.is_some_and(|name| name == static_expr.property.name.as_str()) {
314+
if has_type_annotation {
315+
ctx.diagnostic(prefer_object_destructuring(init.span()));
316+
} else {
286317
ctx.diagnostic_with_fix(
287318
prefer_object_destructuring(init.span()),
288319
|fixer| {
289320
generate_fix(
290321
&fixer,
291-
string_literal.span.shrink(1),
322+
static_expr.property.span,
292323
get_object_span_without_redundant_parentheses(
293-
&comp_expr.object,
324+
&static_expr.object,
294325
),
295326
declarator.span(),
296327
)
297328
},
298329
);
299330
}
300-
}
301-
}
302-
MemberExpression::StaticMemberExpression(static_expr)
303-
if self.variable_declarator.object =>
304-
{
305-
if self.enforce_for_renamed_properties {
331+
} else if self.enforce_for_renamed_properties {
306332
ctx.diagnostic(prefer_object_destructuring(right.span()));
307333
}
308-
if name.is_some_and(|name| name == static_expr.property.name.as_str()) {
309-
ctx.diagnostic_with_fix(
310-
prefer_object_destructuring(init.span()),
311-
|fixer| {
312-
generate_fix(
313-
&fixer,
314-
static_expr.property.span,
315-
get_object_span_without_redundant_parentheses(
316-
&static_expr.object,
317-
),
318-
declarator.span(),
319-
)
320-
},
321-
);
322-
}
323334
}
324335
_ => {}
325336
}
@@ -382,6 +393,26 @@ fn test() {
382393
let pass = vec![
383394
("var [foo] = array;", None),
384395
("var { foo } = object;", None),
396+
("const foo: string = object.foo;", None),
397+
("const foo: string = object['foo'];", None),
398+
("const foo: string = array[0];", None),
399+
(
400+
"const object = { foo: 'value' as const }; const foo: string = object.foo;",
401+
Some(serde_json::json!([
402+
{
403+
"VariableDeclarator": { "array": false, "object": true },
404+
"AssignmentExpression": { "array": false, "object": false }
405+
},
406+
{
407+
"enforceForDeclarationWithTypeAnnotation": false,
408+
"enforceForRenamedProperties": false
409+
}
410+
])),
411+
),
412+
(
413+
"const foo: string = object.foo;",
414+
Some(serde_json::json!([{ "object": true }, { "enforceForRenamedProperties": true }])),
415+
),
385416
(
386417
"a = b.c",
387418
Some(
@@ -584,6 +615,44 @@ fn test() {
584615
("var foo = array[0];", None),
585616
("foo = array[0];", None),
586617
("var foo = object.foo;", None),
618+
(
619+
"var foo: string = object.foo;",
620+
Some(
621+
serde_json::json!([{ "object": true }, { "enforceForDeclarationWithTypeAnnotation": true }]),
622+
),
623+
),
624+
(
625+
"var foo: string = object['foo'];",
626+
Some(
627+
serde_json::json!([{ "object": true }, { "enforceForDeclarationWithTypeAnnotation": true }]),
628+
),
629+
),
630+
(
631+
"var foo: string = array[0];",
632+
Some(
633+
serde_json::json!([{ "array": true }, { "enforceForDeclarationWithTypeAnnotation": true }]),
634+
),
635+
),
636+
(
637+
"var foo: string = object.foo;",
638+
Some(serde_json::json!([
639+
{ "object": true },
640+
{
641+
"enforceForDeclarationWithTypeAnnotation": true,
642+
"enforceForRenamedProperties": true
643+
}
644+
])),
645+
),
646+
(
647+
"var foo: string = object['foo'];",
648+
Some(serde_json::json!([
649+
{ "object": true },
650+
{
651+
"enforceForDeclarationWithTypeAnnotation": true,
652+
"enforceForRenamedProperties": true
653+
}
654+
])),
655+
),
587656
("var foo = (a, b).foo;", None),
588657
("var length = (() => {}).length;", None),
589658
("var foo = (a = b).foo;", None),
@@ -739,6 +808,20 @@ fn test() {
739808
),
740809
("var foo = object.foo, /* comment */ a;", "var {foo} = object, /* comment */ a;", None),
741810
("var foo = object['foo'];", "var {foo} = object;", None),
811+
(
812+
"var foo: string = object.foo;",
813+
"var foo: string = object.foo;",
814+
Some(
815+
serde_json::json!([{ "object": true }, { "enforceForDeclarationWithTypeAnnotation": true }]),
816+
),
817+
),
818+
(
819+
"var foo: string = object['foo'];",
820+
"var foo: string = object['foo'];",
821+
Some(
822+
serde_json::json!([{ "object": true }, { "enforceForDeclarationWithTypeAnnotation": true }]),
823+
),
824+
),
742825
("foo = object.foo;", "foo = object.foo;", None),
743826
("foo = object['foo'];", "foo = object['foo'];", None),
744827
];

‎crates/oxc_linter/src/snapshots/eslint_prefer_destructuring.snap‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,41 @@ source: crates/oxc_linter/src/tester.rs
2323
╰────
2424
help: Use object destructuring rather than direct member access.
2525

26+
eslint(prefer-destructuring): Use Object destructuring.
27+
╭─[prefer_destructuring.tsx:1:19]
28+
1var foo: string = object.foo;
29+
· ──────────
30+
╰────
31+
help: Use object destructuring rather than direct member access.
32+
33+
eslint(prefer-destructuring): Use Object destructuring.
34+
╭─[prefer_destructuring.tsx:1:19]
35+
1var foo: string = object['foo'];
36+
· ─────────────
37+
╰────
38+
help: Use object destructuring rather than direct member access.
39+
40+
eslint(prefer-destructuring): Use Array destructuring.
41+
╭─[prefer_destructuring.tsx:1:19]
42+
1var foo: string = array[0];
43+
· ────────
44+
╰────
45+
help: Use array destructuring rather than direct member access.
46+
47+
eslint(prefer-destructuring): Use Object destructuring.
48+
╭─[prefer_destructuring.tsx:1:19]
49+
1var foo: string = object.foo;
50+
· ──────────
51+
╰────
52+
help: Use object destructuring rather than direct member access.
53+
54+
eslint(prefer-destructuring): Use Object destructuring.
55+
╭─[prefer_destructuring.tsx:1:19]
56+
1var foo: string = object['foo'];
57+
· ─────────────
58+
╰────
59+
help: Use object destructuring rather than direct member access.
60+
2661
eslint(prefer-destructuring): Use Object destructuring.
2762
╭─[prefer_destructuring.tsx:1:11]
2863
1var foo = (a, b).foo;

‎npm/oxlint/configuration_schema.json‎

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5987,7 +5987,7 @@
59875987
"$ref": "#/definitions/PreferDestructuringOption"
59885988
},
59895989
{
5990-
"$ref": "#/definitions/PreferDestructuringRenamedPropertiesConfig"
5990+
"$ref": "#/definitions/PreferDestructuringEnforcementConfig"
59915991
}
59925992
],
59935993
"maxItems": 3,
@@ -17016,12 +17016,26 @@
1701617016
"$ref": "#/definitions/PreferDestructuringOption"
1701717017
},
1701817018
{
17019-
"$ref": "#/definitions/PreferDestructuringRenamedPropertiesConfig"
17019+
"$ref": "#/definitions/PreferDestructuringEnforcementConfig"
1702017020
}
1702117021
],
1702217022
"maxItems": 2,
1702317023
"minItems": 2
1702417024
},
17025+
"PreferDestructuringEnforcementConfig": {
17026+
"type": "object",
17027+
"properties": {
17028+
"enforceForDeclarationWithTypeAnnotation": {
17029+
"default": false,
17030+
"type": "boolean"
17031+
},
17032+
"enforceForRenamedProperties": {
17033+
"default": false,
17034+
"type": "boolean"
17035+
}
17036+
},
17037+
"additionalProperties": false
17038+
},
1702517039
"PreferDestructuringOption": {
1702617040
"anyOf": [
1702717041
{
@@ -17032,16 +17046,6 @@
1703217046
}
1703317047
]
1703417048
},
17035-
"PreferDestructuringRenamedPropertiesConfig": {
17036-
"type": "object",
17037-
"properties": {
17038-
"enforceForRenamedProperties": {
17039-
"default": false,
17040-
"type": "boolean"
17041-
}
17042-
},
17043-
"additionalProperties": false
17044-
},
1704517049
"PreferDestructuringTargetOption": {
1704617050
"type": "object",
1704717051
"properties": {

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL