Skip to content

feat: Add `no-implicit-coercion` rule

Nathan Friend requested to merge nfriend-add-no-implicit-coercion into master

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

Edited by Nathan Friend

Merge request reports