Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Rule: prefer-destructuring #1931

Closed
0xCLARITY opened this issue Apr 24, 2020 · 18 comments
Closed

New Rule: prefer-destructuring #1931

0xCLARITY opened this issue Apr 24, 2020 · 18 comments
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: new base rule extension New base rule extension required to handle a TS specific case package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@0xCLARITY
Copy link
Contributor

I'd like to use the prefer-destructuring rule from ESLint, however, when I try to use array destructuring with an Express request object, I get the following TypeScript Error:

Type 'ParamsDictionary' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488).

That seems reasonable, but then the base ESLint prefer-destructuring rule gives me a warning, even though TypeScript claims I'm not allowed to use array destructuring here.

I think having a type-aware version of this rule would be beneficial.

@0xCLARITY 0xCLARITY added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for maintainers to take a look labels Apr 24, 2020
@bradzacher
Copy link
Member

could you please give me a self-contained example?

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed triage Waiting for maintainers to take a look labels Apr 24, 2020
@0xCLARITY
Copy link
Contributor Author

Does this work?

import { Request, Response, NextFunction } from 'express'

async function example(
  req: Request,
  _res: Response,
  _next: NextFunction,
): Promise<void> {
  // Type 'ParamsDictionary' must have a '[Symbol.iterator]()' method that returns an iterator
  const [param] = req.params

  console.log(param)
}

@bradzacher
Copy link
Member

bradzacher commented Apr 24, 2020

what was the code before you fixed it?
const param = req.params[0]?

@0xCLARITY
Copy link
Contributor Author

Yes, which the base ESLint prefer-destructuring rule warns against.

@bradzacher
Copy link
Member

Thanks.
Happy to accept a PR if you'd like to add a type-aware version of the rule to prevent array destructuring of non-iterable types.
In that case, could also implement a fixer, as we know for sure the type is fixable.

@bradzacher bradzacher added enhancement: new base rule extension New base rule extension required to handle a TS specific case and removed awaiting response Issues waiting for a reply from the OP or another party labels Apr 24, 2020
@phaux
Copy link
Contributor

phaux commented Jul 3, 2020

The correct way to destructure this is

const { 0: param } = req.params

because params is just an object with numeric properties.

@danvk
Copy link

danvk commented Aug 20, 2020

Another issue with the eslint prefer-destructuring rule in TS is if you apply a type declaration to the resulting variable:

const obj = {xs: ['a', 'b', 'c']};  // type is {xs: string[]}
const xs: readonly string[] = obj.xs;
xs.push('d');
// ~~~~ Property 'push' does not exist on type 'readonly string[]'.

Line two gets flagged by eslint's rule:

image

However, applying the quick fix removes the type annotation, an unsafe operation which makes the push call pass the type checker:

const obj = {xs: ['a', 'b', 'c']};  // type is {xs: string[]}
const {xs} = obj;
xs.push('d');  // ok! oh no!

A typescript-eslint prefer-destructuring rule should ignore assignments with attached type declarations.

@rafaelss95
Copy link
Contributor

Isn't it a duplication of #723? This one had a lot more interaction than the former though.

@bradzacher
Copy link
Member

bradzacher commented Oct 5, 2021

No - they both want to extend the same extension rule - but they want different things.

THIS issue is about
const x = (y as {0: unknown})[0]
being fixed to
const [x] = (y as {0: unknown})
i.e. the rule causes a type error due to attempting to convert a non-iterable to an array destructure.

#723 is about
const x: number = (y as {bar: 1}).bar
being fixed to
const {x} = (y as {bar: 1})
i.e. the rule erases the type annotation on the variable.

@rafaelss95
Copy link
Contributor

rafaelss95 commented Oct 11, 2021

Oh yes, I noticed that too, but as (#1931 (comment)) was talking about the same of #723 (and received so many upvotes), I thought all related things were being tracked here.

@JoshuaKGoldberg JoshuaKGoldberg added the accepting prs Go ahead, send a pull request that resolves this issue label Oct 25, 2021
jessety added a commit to jessety/eslint-config that referenced this issue Jan 16, 2022
- DIsable `prefer-destructuring` in TypeScript, as it is potentially desctructive typescript-eslint/typescript-eslint#1931
- Set any rules that could conflict with Prettier to match @jessety/prettier-config
- Disable `@typescript-eslint/no-inferrable-types` - I've found it to be unhelpful
- Fix issue where JS tests were treated like TS tests

New rules
- `lines-between-class-members`
- Set `import/no-default-export` to `warn`
@iliubinskii
Copy link

Another issue with core eslint-rule:

export const construct: Function = Reflect.construct;

// Fixed to:
export const { construct } = Reflect;

As you can see, core rule auto-fix removes type definition (": Function").

@seiyab
Copy link
Contributor

seiyab commented Jun 15, 2023

Hi, I'm working on this issue.
For the original problem, I want to clarify the desired behavior.
I'm considering following behavior. Is it reasonable?

const a = {0: 1};
const b = a[0]; // this is incorrect iff options[0].object is true and options[1].enforceForRenamedProperties  is true
const {0: c} = a; // this is always correct


const x = [1];
const y = x[0]; // works same as the current ESLint rule

const v: any = {0: 1};
const w = x[0]; // works same as the current ESLint rule

Showing other edge cases are welcome.

@seiyab
Copy link
Contributor

seiyab commented Jun 15, 2023

I'm also working on #1931 (comment), #1931 (comment) and #723.
It is almost done in my local repository. Wait for a little while 😉 .

@JoshuaKGoldberg
Copy link
Member

I'm considering following behavior. Is it reasonable?

Sorry, I don't follow what you're asking about @seiyab. There are no rule reports from the base prefer-destructuring rule for const b = a[0];.

@seiyab
Copy link
Contributor

seiyab commented Aug 6, 2023

@JoshuaKGoldberg
I'm sorry for unclearness. (My English might often be broken.)
The base prefere-destructuring doesn't report for const b = a[0] if array in options is false, as you wrote.
For the following example, I wanted to show the behavior of the new rule I'm considering (and have already implemented as a PR).

const a = {0: 1};
const b = a[0]; // this is incorrect iff options[0].object is true and options[1].enforceForRenamedProperties  is true

In this case, the proper destructuring syntax is const { 0: b } = a rather than const [b] = a since a is not iterable. This should be the primary point in this issue.
The comment following const b = a[0] suggests:

  • options[0].object should control the report. options[1].array shouldn't.
  • it should report it only if options[1].enforceForRenamedProperties because it is renamed from 0 to b

Related document I have written: https://github.com/typescript-eslint/typescript-eslint/pull/7117/files#diff-0d683d5803f89de0a159ca7ef7461802afac69ae7fbf459411553aa36e5a7cbaR49-R50

And I asked that whether my thoughts make sense or not.

Does it get clear?

@seiyab
Copy link
Contributor

seiyab commented Aug 6, 2023

"this will be incorrect iff ..." might be better than "this is incorrect iff ...".

@tjx666
Copy link

tjx666 commented Aug 31, 2023

Example bad case for ESLint builtin prefer-destructuring:

image

After destructuring:

image

@HolgerJeromin
Copy link

Can this issue be closed? ref PR #7117

@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale Oct 17, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: new base rule extension New base rule extension required to handle a TS specific case package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
Development

No branches or pull requests