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

feat(eslint-plugin): [no-unsafe-array-reduce] Add rule #1779

Closed

Conversation

asmundg
Copy link

@asmundg asmundg commented Mar 21, 2020

Add rule for protecting against unsafe array reduce calls.

The type of {} is {}. Any object is assignable to {}. {} is assignable to
any indexed type ({[key in string]: whatever}).

A reduce call with an empty object initializer and no type signature,
will infer the {} type for the accumulator and result of the reduce
expression. Since anything is assignable to {}, this means the reduce
function is essentially unchecked. The result of the expression can then
also be assigned to an incompatible type without raising any errors.

This rule warns if a reduce call takes an empty object as the initial
value and has no type signatures.

Add rule for protecting against unsafe array reduce calls.

The type of {} is {}. Any object is assignable to {}. {} is assignable
to any indexed type (`{[key in string]: whatever}`)

A reduce call with an empty object initializer and no type signature,
will infer the {} type for the accumulator and result of the reduce
expression. Since anything is assignable to {}, this means the reduce
function is essentially unchecked. The result of the expression can then
also be assigned to an incompatible type without raising any errors.

This rule warns if a reduce call takes an empty object as the initial
value and has no type signatures.
@typescript-eslint
Copy link
Contributor

Thanks for the PR, @asmundg!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day.

@codecov
Copy link

codecov bot commented Mar 21, 2020

Codecov Report

Merging #1779 into master will decrease coverage by 0.20%.
The diff coverage is 79.88%.

@@            Coverage Diff             @@
##           master    #1779      +/-   ##
==========================================
- Coverage   95.20%   95.00%   -0.21%     
==========================================
  Files         148      159      +11     
  Lines        6989     7080      +91     
  Branches     2017     2021       +4     
==========================================
+ Hits         6654     6726      +72     
- Misses        124      154      +30     
+ Partials      211      200      -11     
Flag Coverage Δ
#unittest 95.00% <79.88%> (-0.21%) ⬇️
Impacted Files Coverage Δ
...es/eslint-plugin/src/rules/no-array-constructor.ts 100.00% <ø> (ø)
...s/eslint-plugin/src/rules/prefer-optional-chain.ts 95.09% <ø> (-0.05%) ⬇️
...nt-plugin/src/rules/switch-exhaustiveness-check.ts 100.00% <ø> (ø)
...perimental-utils/src/ts-eslint-scope/Definition.ts 100.00% <ø> (ø)
...mental-utils/src/ts-eslint-scope/PatternVisitor.ts 100.00% <ø> (ø)
...xperimental-utils/src/ts-eslint-scope/Reference.ts 100.00% <ø> (ø)
...perimental-utils/src/ts-eslint-scope/Referencer.ts 100.00% <ø> (ø)
...es/experimental-utils/src/ts-eslint-scope/Scope.ts 100.00% <ø> (ø)
...rimental-utils/src/ts-eslint-scope/ScopeManager.ts 100.00% <ø> (ø)
...experimental-utils/src/ts-eslint-scope/Variable.ts 100.00% <ø> (ø)
... and 39 more

@bradzacher bradzacher added the enhancement: new plugin rule New rule request for eslint-plugin label Mar 22, 2020
@bradzacher
Copy link
Member

I'm curious what the motivation behind this rule is.
With noImplicitAny turned on, attempting to do an index access on a variable typed as {} results in a TS error:

  const x = ['a'].reduce((acc, cur) => acc[cur], {});
//                                     ^^^^^^^^
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
//  No index signature with a parameter of type 'string' was found on type '{}'.(7053)

Additionally, {} has no properties, so you can't do . member access on it.

It seems fairly safe without a lint rule.
There is also #1707, which is similar to this rule.

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Mar 24, 2020
@asmundg
Copy link
Author

asmundg commented Mar 24, 2020

See https://www.typescriptlang.org/play/index.html#code/MYewdgzgLgBAZiEAuGBvGBtA1gUwJ4wCWYM0ATsQOYC6A-CuVTAL4wC8mARLnp9QHRkcAEwCuwHAApJAQ2DAANDGCiyASnYA+GJPT99cxZhVlqKAIws1S1MzUAoewHonMAEqiSUQgFscMABU8AAccAFEyMhAyewQQfh5afmgZMigIAHVCKAALSU5ONSA for a minimal example of how untyped reduce with a {} initial value can blow up at runtime.

You're right that you can't read indexes of the {} type, but you can assign to its indexes and you can assign it to another indexed type, bypassing the type checking in a way that none of the strict compiler options will catch.

#1707 is related, but it deals with where the type argument is provided. Which seems like more of an aesthetic issue than requiring there to be a type argument like we do here.

@bradzacher
Copy link
Member

I don't think creating a rule that just handles the case of an array reduce is a good solution to this, as this can happen in more places than just a reduce.

let x: {} = {};
x = { ...x, a: 1 };
const y: Record<string, string> = x;

The specific problem here is that {} can receive any object-like type, and be similarly assigned to any object-like type.
So a better solution would be to have a rule that bans the assignment of an {} typed value anywhere, to help catch this unsafety.

@asmundg
Copy link
Author

asmundg commented Mar 24, 2020

I agree in principle. This is reduce-oriented since

  1. That's where I've seen people make this mistake
  2. I'm not sure how a rule banning assignment of the {} type would be implemented

If you have some pointers on 2. that's definitely something worth looking into. :)

@bradzacher
Copy link
Member

Sorry, this got lost in my notifications.

This could actually live in the no-unsafe-* rules.
We could treat {} as an additional unsafe type (same as those rules treat any).
That would save you having to implement it from ground up!

@bradzacher bradzacher added the stale PRs or Issues that are at risk of being or have been closed due to inactivity for a prolonged period label Oct 4, 2020
@bradzacher bradzacher closed this Oct 4, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party enhancement: new plugin rule New rule request for eslint-plugin stale PRs or Issues that are at risk of being or have been closed due to inactivity for a prolonged period
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants