Skip to content

Bug: [prefer-reduce-type-parameter] Quick-fix mis-handles null-prototype objects #7510

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

Closed
4 tasks done
nickshanks opened this issue Aug 20, 2023 · 5 comments
Closed
4 tasks done
Labels
bug Something isn't working duplicate This issue or pull request already exists package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@nickshanks
Copy link

nickshanks commented Aug 20, 2023

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Playground Link

https://typescript-eslint.io/play/#ts=5.2.1-rc&fileType=.tsx&code=NoXQdATgpgJgrgYygCgB4AIC8A%2BdqA06A3ugPqkAOEA9gC7XkBc6AdnADbvoC%2B6AhgGd0cFgGsW1AO4t%2BQgEpQE1CDAA8A2hACWLAOaEN2vdgCUAbgBQQA&eslintrc=N4KABGBEAOCGBOBnApvSAuKABALgT2mUQGN4BLaHAWiIBsyA7HAejiVUgBpwo2V4A8pTIB7BogxhQECDHgiAVsmI5JOeAFdk3GVBwkxAMzIBzAEoiROACJk0mSAH1HAEzsNYAW2SQeAXx0oTVoiSWkZSFwCIlIKajpGFmh4ZENUKhSXDWJkKnxCKjYvZBwOB1R5NH8QPyA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false

Repro Code

[].reduce(x=>x, { __proto__: null } as unknown as Record<string, string>);

ESLint Config

module.exports = {
  parser: "@typescript-eslint/parser",
  rules: {
    "@typescript-eslint/prefer-reduce-type-parameter": ["error"],
  },
};

tsconfig

{
  "compilerOptions": {
    // ...
  }
}

Expected Result

Quick Fix would transform the code to:

[].reduce<Record<string, string>>(x=>x, Object.create(null));

Actual Result

After applying Quick Fix:

[].reduce<Record<string, string>>(x=>x, { __proto__: null } as unknown);
                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"Argument of type 'unknown' is not assignable to parameter of type 'Record<string, string>'. ts(2345)"

Then after second Quick Fix:

[].reduce<Record<string, string>>(x=>x, { __proto__: null });
                                          ~~~~~~~~~

"Type 'null' is not assignable to type 'string'. ts(2322)"

Additional Info

Object.create(null) does not have the TypeScript problems that { __proto__: null } does, and the latter should be transformed into the former before the pair of Quick Fixes are applied. All three transformations are required for eslint --fix to not break anything.

@nickshanks nickshanks added bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Aug 20, 2023
@bradzacher
Copy link
Member

Same as #3440

For the Object.create vs __proto__ - our rules won't make that autofix ever. It's not on our rules to change your code like that.

The former only works because it returns any - so you could have any type at all and it would work.

There's no fix here that would actually work because TS does not understand that __proto__ isn't a real property. Your code works because the unknown assertion hides the fact that the object is typed as Record<string, null> which then allows you to unsafely assert the type to a Record<string, string>.

@bradzacher bradzacher added duplicate This issue or pull request already exists and removed triage Waiting for team members to take a look labels Aug 20, 2023
@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale Aug 20, 2023
@nickshanks
Copy link
Author

nickshanks commented Aug 21, 2023

@bradzacher, then can you instead ignore reduce() calls where the initial object uses __proto__?
Because at present, "@typescript-eslint/prefer-reduce-type-parameter": ["error"], will result in a broken auto-fix.

@bradzacher
Copy link
Member

Using __proto__ is not unique to this problem. There are a few things that result in a broken autofix, as per the above issue.

It's something that needs to be fixed.

Adding support specifically for objects that define the proto property is not going to be a path we'll take though. In modern TS it's incredibly rare and super niche. TBH using a prototype-less object in any declaration form is super niche!

Instead we'll look for a general purpose solution that solve more, if not all the problems.

There is also general problems to do with missing APIs in TS that make solving this problem difficult so we may need to decide on interim solutions.

As with anything in this project - it won't progress without a community member to champion it. We volunteer maintainers have limited bandwidth that is mostly consumed with triage, infra and upgrades; so we look to our community members to help implement things that are important to them.

Feel free to hop across to the other issue if you want to be that champion.

@nickshanks
Copy link
Author

nickshanks commented Aug 21, 2023

Thank you for your time (though I assumed the devs on this project were working for Microsoft and this was part of their day-job).

I am using null-prototype objects anywhere I have key-value maps where the keys are user-supplied. The use case I had here was multipart/form-data input names, but also many other places in the rest of my HTTP stack. Is this not best practice? It's what JS itself does.

I feel strongly that there shouldn't be an auto-fix available if it might sometimes break the code. I was trying to come up with a solution so that the auto-fix could be retained, but it looks like it will need to be disabled until #3440 gets implemented.

FWIW, I had read all the way through #3440 before decided my use case was different and thinking it was not unsound as theirs was. Oh well.

@bradzacher
Copy link
Member

Object.create(null) is the best practice - but yes you're correct that when storing user-defined keys in an object removing the prototype is the best practice to avoid accidental overwrites. Though it's worth noting that TS does not model the null prototype and thus you can easily accidentally write broke code!


Your usecase is runtime sound but typecheck unsound - hence the need for a cast through unknown!

I feel strongly that there shouldn't be an auto-fix available if it might sometimes break the code.

That's the best practice and what we strive for! But often times rules see lower usages or bad/broken fix cases are rare which means that there isn't enough drive or motivation for the community to fix it!

Disabling the fix for certain cases is a valid fix for the problem. But it takes work to do that, which requires community effort.

though I assumed the devs on this project were working for Microsoft and this was part of their day-job

Nope none of the maintainers work for or are paid by Microsoft. The project isn't even OSS sponsored my Microsoft!
It's a common misconception!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working duplicate This issue or pull request already exists package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
Development

No branches or pull requests

2 participants