feat: Add `no-implicit-coercion` rule
Adds the no-implicit-coercion ESLint rule with all options enabled. This rule prevents type coercion shortcuts that can be difficult to understand:
var b = !!foo;
var b = ~foo.indexOf(".");
var n = +foo;
var n = 1 * foo;
var s = "" + foo;
foo += ``;
Right now, we use both Boolean(...) and !! throughout our codebase - this change would allow us to standardize on a single style (Boolean(...)).
This change also disallows some of the shortcuts above related to String and Number conversions. My original goal was to standardize on Boolean(...) vs !!, but these additional changes seem like a good idea as well, so I'm includinging them in this merge request. If this is an issue, I can reduce the scope of the merge request to only the Boolean conversions.
All errors can be auto-fixed by ESLint, which makes this rule easy to implement. Currently there are 92 violations of this rule in EE:
Click here to expand the result of running ESLint on EE with this new rule enabled
~/source/gdk-ee/gitlab (master) $ yarn eslint
yarn run v1.16.0
$ eslint --max-warnings 0 --report-unused-disable-directives --ext .js,.vue .
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
40:24 error use `Boolean(documentFragment.querySelector('.md'))` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/boards/components/board_list.vue
146:70 error use `Number(e.item.dataset.issueId)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/boards/components/modal/index.vue
127:30 error use `Boolean(foundSelectedIssue)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/boards/models/list.js
40:19 error use `Boolean(typeInfo.isPreset)` instead no-implicit-coercion
41:25 error use `Boolean(typeInfo.isExpandable)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/branches/branches_delete_modal.js
26:21 error use `Boolean(branchData.isMerged)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/clusters/components/application_row.vue
141:14 error use `Boolean(this.logoUrl)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/compare_autocomplete.js
43:21 error use `Boolean($dropdown.data('refsUrl'))` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/create_item_dropdown.js
15:26 error use `Boolean(options.filterRemote)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/error_tracking_settings/store/getters.js
5:37 error use `Boolean(state.projects)` instead no-implicit-coercion
8:3 error use `Boolean(state.selectedProject)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/frequent_items/store/actions.js
54:17 error use `Boolean(gon.current_user_id)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/gl_dropdown.js
310:22 error use `Boolean(this.options.highlight)` instead no-implicit-coercion
311:17 error use `Boolean(this.options.icon)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/gl_form.js
16:32 error use `Boolean(dataSources[item])` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/components/preview/clientside.vue
108:44 error use `Boolean(this.entries[createPathWithExt(p)])` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/components/repo_commit_section.vue
33:14 error use `Boolean(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/lib/editor_options.js
14:24 error use `Boolean(model.file.file_lock)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/stores/getters.js
45:36 error use `Boolean(state.changedFiles.length)` instead no-implicit-coercion
45:67 error use `Boolean(state.stagedFiles.length)` instead no-implicit-coercion
47:41 error use `Boolean(state.currentMergeRequestId)` instead no-implicit-coercion
73:3 error use `Boolean(state.changedFiles.length || state.stagedFiles.length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/stores/modules/commit/actions.js
105:16 error use `Boolean(changedFile)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/stores/modules/pipelines/getters.js
3:71 error use `Boolean(state.latestPipeline)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/ide/stores/mutations.js
145:16 error use `Boolean(changedFile)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
17:22 error use `Boolean(commentIndicatorEl)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/image_diff/image_diff.js
9:26 error use `Boolean(options && options.canCreateNote)` instead no-implicit-coercion
10:31 error use `Boolean(options && options.renderCommentBadge)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/image_diff/view_types.js
8:10 error use `Boolean(Object.getOwnPropertyNames(viewTypes).find(viewType => viewType === validate))` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/issuable_index.js
15:32 error use `Boolean(this.bulkUpdateSidebar)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/issue_show/components/app.vue
159:14 error use `Boolean(this.state.updatedAt)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/label_manager.js
56:7 error use `Boolean(this.prioritizedLabels[0].querySelector(':scope > li'))` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/lib/utils/accessor.js
5:12 error use `Boolean(base[property])` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/lib/utils/datetime_utility.js
516:25 error use `Boolean(unitValue)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/lib/utils/text_markdown.js
226:20 error use `String(val.replace(tag, ''))` instead no-implicit-coercion
228:20 error use `String(tag)` instead no-implicit-coercion
236:20 error use `String(startChar)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/mr_notes/stores/getters.js
3:12 error use `Boolean(getters.getUserData.id)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/notes/components/discussion_notes.vue
52:14 error use `Boolean(this.replies.length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/notes/components/noteable_note.vue
78:14 error use `Boolean(this.note.report_abuse_path)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/notes/mixins/issuable_state.js
4:14 error use `Boolean(issue.confidential)` instead no-implicit-coercion
8:14 error use `Boolean(issue.discussion_locked)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/notes/stores/getters.js
23:38 error use `Boolean(state.noteableData.current_user.can_create_note)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
32:14 error use `Boolean(this.customInputEnabled || !this.intervalIsPreset)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/profile/account/index.js
38:32 error use `Boolean(deleteAccountModalEl.dataset.confirmWithPassword)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/projects/gke_cluster_dropdowns/store/actions.js
60:50 error use `Boolean(billingEnabled)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/projects/gke_cluster_dropdowns/store/getters.js
1:36 error use `Boolean(state.selectedProject.projectId)` instead no-implicit-coercion
2:33 error use `Boolean(state.selectedZone)` instead no-implicit-coercion
3:40 error use `Boolean(state.selectedMachineType)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/registry/stores/mutations.js
12:20 error use `Boolean(el.destroy_path)` instead no-implicit-coercion
45:18 error use `Boolean(element.destroy_path)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/right_sidebar.js
85:11 error use `String($this.data('deletePath'))` instead no-implicit-coercion
87:11 error use `String($this.data('createPath'))` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/search_autocomplete.js
382:40 error use `Boolean(e.target.value)` instead no-implicit-coercion
399:40 error use `Boolean(e.target.value)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
52:14 error use `Boolean(this.timeSpent)` instead no-implicit-coercion
55:14 error use `Boolean(this.timeEstimate)` instead no-implicit-coercion
70:14 error use `Boolean(this.showHelp)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
80:14 error use `Boolean(this.deployment.external_url && this.deployment.external_url_formatted)` instead no-implicit-coercion
83:14 error use `Boolean(this.deployment.deployed_at && this.deployment.deployed_at_formatted)` instead no-implicit-coercion
86:14 error use `Boolean(this.deployment.url && this.deployment.name)` instead no-implicit-coercion
89:14 error use `Boolean(this.deployment.metrics_url)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
59:14 error use `Boolean(this.mr.visualReviewFF && this.mr.visualReviewAppAvailable)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
100:14 error use `Boolean(this.mr.relatedLinks)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
81:28 error use `Boolean(data.should_be_rebased)` instead no-implicit-coercion
94:21 error use `Boolean(data.merge_path)` instead no-implicit-coercion
96:36 error use `Boolean(data.cancel_merge_when_pipeline_succeeds_path)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_shared/components/pikaday.vue
37:23 error use `Boolean(this.selectedDate)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/app/assets/javascripts/vue_shared/components/table_pagination.vue
124:23 error use `Number(text)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/babel.config.js
42:16 error use `Boolean(process.env.JEST_WORKER_ID)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/approvals/mappers.js
18:14 error use `Boolean(res.source_rule)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/batch_comments/stores/modules/batch_comments/getters.js
32:3 error use `Boolean(diffFileSha in getters.draftsPerFileHashAndLine &&
getters.draftsPerFileHashAndLine[diffFileSha][line.line_code])` instead no-implicit-coercion
41:26 error use `Boolean(draftsForFile[lkey] || draftsForFile[rkey])` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/burndown_chart/burndown_chart.js
178:34 error use `Boolean(animate)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
58:14 error use `Boolean(this.queryIsValid &&
this.title.length &&
this.yLabel.length &&
this.unit.length &&
this.group.length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/epic/sidebar_context.js
23:27 error use `Boolean($selectbox.get(0).offsetParent)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/epic/store/getters.js
9:37 error use `Boolean(gon.current_user_id)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/monitoring/components/alert_widget.vue
65:14 error use `Boolean(Object.keys(this.alertsToManage).length)` instead no-implicit-coercion
68:14 error use `Boolean(this.errorMessage || this.isLoading)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/multiple_rule/approvals_summary.vue
54:14 error use `Boolean(this.approvers.length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/multiple_rule/approvals.vue
53:14 error use `Boolean(this.approvals.has_approval_rules)` instead no-implicit-coercion
59:14 error use `Boolean(this.approvals.approved)` instead no-implicit-coercion
68:14 error use `Boolean(this.approvals.user_has_approved)` instead no-implicit-coercion
71:14 error use `Boolean(this.approvals.user_can_approve)` instead no-implicit-coercion
109:14 error use `Boolean(this.action)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
71:26 error use `Boolean(data.approvals_left)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/vue_shared/components/linked_pipelines_mini_list.vue
39:14 error use `Boolean(this.triggeredBy.length)` instead no-implicit-coercion
42:42 error use `Boolean(this.triggered.length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/ee/app/assets/javascripts/vue_shared/license_management/components/license_management_row.vue
27:9 error use `Boolean(license.name)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/spec/frontend/helpers/vue_test_utils_helper.js
19:3 error use `Boolean(shallowWrapper.vm.$slots[slotName].filter(vnode => vNodeContainsText(vnode, text)).length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/spec/javascripts/helpers/vue_test_utils_helper.js
19:3 error use `Boolean(shallowWrapper.vm.$slots[slotName].filter(vnode => vNodeContainsText(vnode, text)).length)` instead no-implicit-coercion
/Users/nathanfriend/source/gdk-ee/gitlab/spec/javascripts/matchers.js
31:15 error use `Boolean(matchingIcon)` instead no-implicit-coercion
✖ 92 problems (92 errors, 0 warnings)
92 errors and 0 warnings potentially fixable with the `--fix` option.
error Command failed with exit code 1.
Context: this Slack thread (internal only)
Fixes
Here's an MR to fix all of these violations: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13803
And here's the CE backport of the above: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29007