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

Support Optional Chaining #511

Closed
Akryum opened this issue Aug 22, 2017 · 34 comments

Comments

@Akryum
Copy link

commented Aug 22, 2017

(From eslint/eslint#9124)

Tell us about your environment

  • ESLint Version: 4.4.1
  • Node Version: 8.2.1
  • yarn Version: 0.27.5

What parser (default, Babel-ESLint, etc.) are you using?

Babel-ESLint (tried with 7.2.3 and 8.0.0-alpha.18)

Please show your full configuration:

Configuration
module.exports = {
  root: true,
  extends: [
    'eslint:recommended',
    'plugin:vue/recommended',
  ],
  'parserOptions': {
    'parser': 'babel-eslint',
    'sourceType': 'module',
  },
  env: {
    es6: true,
    node: true,
    browser: true,
  },
  // add your custom rules here
  'rules': {
    'accessor-pairs': 'error',
    'array-callback-return': 'warn',
    'arrow-spacing': ['error', { 'before': true, 'after': true }],
    'block-spacing': ['error', 'always'],
    'brace-style': ['error', '1tbs', { 'allowSingleLine': false }],
    'camelcase': ['error', { 'properties': 'never' }],
    'comma-dangle': ['error', 'always-multiline'],
    'comma-spacing': ['error', { 'before': false, 'after': true }],
    'comma-style': ['error', 'last'],
    'constructor-super': 'error',
    'curly': ['error', 'multi-line'],
    'dot-location': ['error', 'property'],
    'eol-last': 'error',
    'eqeqeq': ['error', 'always', { 'null': 'ignore' }],
    'func-call-spacing': ['error', 'never'],
    'handle-callback-err': ['error', '^(err|error)$' ],
    'indent': ['error', 2, { 'SwitchCase': 1 }],
    'key-spacing': ['error', { 'beforeColon': false, 'afterColon': true }],
    'keyword-spacing': ['error', { 'before': true, 'after': true }],
    'new-cap': ['error', { 'newIsCap': true, 'capIsNew': false }],
    'new-parens': 'error',
    'no-array-constructor': 'error',
    'no-caller': 'error',
    'no-class-assign': 'error',
    'no-compare-neg-zero': 'error',
    'no-cond-assign': 'error',
    'no-console': 'off',
    'no-const-assign': 'error',
    'no-constant-condition': ['error', { 'checkLoops': false }],
    'no-control-regex': 'error',
    'no-delete-var': 'error',
    'no-dupe-args': 'error',
    'no-dupe-class-members': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'error',
    'no-empty-character-class': 'error',
    'no-empty-pattern': 'error',
    'no-eval': 'error',
    'no-ex-assign': 'error',
    'no-extend-native': 'warn',
    'no-extra-bind': 'error',
    'no-extra-boolean-cast': 'error',
    'no-extra-parens': ['error', 'functions'],
    'no-fallthrough': 'error',
    'no-floating-decimal': 'error',
    'no-func-assign': 'error',
    'no-global-assign': 'error',
    'no-implied-eval': 'error',
    'no-inner-declarations': ['error', 'functions'],
    'no-invalid-regexp': 'error',
    'no-irregular-whitespace': 'error',
    'no-iterator': 'error',
    'no-label-var': 'error',
    'no-labels': ['error', { 'allowLoop': false, 'allowSwitch': false }],
    'no-lone-blocks': 'error',
    'no-mixed-operators': ['error', {
      'groups': [
        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
        ['&&', '||'],
        ['in', 'instanceof']
      ],
      'allowSamePrecedence': true
    }],
    'no-mixed-spaces-and-tabs': 'error',
    'no-multi-spaces': 'error',
    'no-multi-str': 'error',
    'no-multiple-empty-lines': ['error', { 'max': 1, 'maxEOF': 0 }],
    'no-negated-in-lhs': 'error',
    'no-new': 'error',
    'no-new-func': 'error',
    'no-new-object': 'error',
    'no-new-require': 'error',
    'no-new-symbol': 'error',
    'no-new-wrappers': 'error',
    'no-obj-calls': 'error',
    'no-octal': 'error',
    'no-octal-escape': 'error',
    'no-path-concat': 'error',
    'no-proto': 'error',
    'no-redeclare': 'error',
    'no-regex-spaces': 'error',
    'no-return-await': 'error',
    'no-self-assign': 'error',
    'no-self-compare': 'error',
    'no-sequences': 'error',
    'no-shadow-restricted-names': 'error',
    'no-sparse-arrays': 'error',
    'no-tabs': 'error',
    'no-template-curly-in-string': 'error',
    'no-this-before-super': 'error',
    'no-throw-literal': 'error',
    'no-trailing-spaces': 'error',
    'no-undef': 'error',
    'no-undef-init': 'error',
    'no-unexpected-multiline': 'error',
    'no-unmodified-loop-condition': 'error',
    'no-unneeded-ternary': ['error', { 'defaultAssignment': false }],
    'no-unreachable': 'error',
    'no-unsafe-finally': 'error',
    'no-unsafe-negation': 'error',
    // 'no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
    'no-unused-vars': ['error', { 'vars': 'all', 'args': 'none', 'ignoreRestSiblings': true }],
    'no-use-before-define': ['error', { 'functions': false, 'classes': false, 'variables': false }],
    'no-useless-call': 'error',
    'no-useless-computed-key': 'error',
    'no-useless-constructor': 'error',
    'no-useless-escape': 'error',
    'no-useless-rename': 'error',
    'no-useless-return': 'error',
    'no-var': 'error',
    'no-whitespace-before-property': 'error',
    'no-with': 'error',
    'object-property-newline': ['error', { 'allowMultiplePropertiesPerLine': true }],
    'one-var': ['error', { 'initialized': 'never' }],
    'operator-linebreak': ['error', 'after', { 'overrides': { '?': 'before', ':': 'before' } }],
    'padded-blocks': ['error', { 'blocks': 'never', 'switches': 'never', 'classes': 'never' }],
    'prefer-const': 'warn',
    'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }],
    'rest-spread-spacing': ['error', 'never'],
    'semi': ['error', 'never'],
    'semi-spacing': ['error', { 'before': false, 'after': true }],
    'space-before-blocks': ['error', 'always'],
    'space-before-function-paren': ['error', 'always'],
    'space-in-parens': ['error', 'never'],
    'space-infix-ops': 'error',
    'space-unary-ops': ['error', { 'words': true, 'nonwords': false }],
    'spaced-comment': ['error', 'always', {
      'line': { 'markers': ['*package', '!', '/', ','] },
      'block': { 'balanced': true, 'markers': ['*package', '!', ',', ':', '::', 'flow-include'], 'exceptions': ['*'] }
    }],
    'symbol-description': 'error',
    'template-curly-spacing': ['error', 'never'],
    'template-tag-spacing': ['error', 'never'],
    'unicode-bom': ['error', 'never'],
    'use-isnan': 'error',
    'valid-typeof': ['error', { 'requireStringLiterals': true }],
    'wrap-iife': ['error', 'any', { 'functionPrototypeMethods': true }],
    'yield-star-spacing': ['error', 'both'],
    'yoda': ['error', 'never'],

    'vue/require-v-for-key': 'off',
    'vue/html-quotes': 'error',
    'vue/v-bind-style': 'error',
    'vue/v-on-style': 'error',
  }
}

.babelrc:

{
  "presets": [
    ["env", { "modules": false }],
    "stage-0"
  ],
  "plugins": [
    "transform-vue-jsx",
    "jsx-vue-functional",
    "jsx-v-model",
    "transform-optional-chaining"
  ]
}

What did you do? Please include the actual source code causing the issue.

if (group.invoices?.length === 0) {

What did you expect to happen?

No error thrown when using optional chaining.

What actually happened? Please include the actual, raw output from ESLint.

Syntax Error: Unexpected token (105:38)

  103 |                 height: 100,
  104 |               })))
> 105 |             } else if (group.invoices?.length === 0) {
      |                                       ^
  106 |               list.push({
  107 |                 type: 'empty',
  108 |                 height: 67,



 @ ./src/components/invoices/invoice-documents/DocumentList.vue 8:0-131
 @ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/invoic
es/invoice-documents/InvoiceDocuments.vue
 @ ./src/components/invoices/invoice-documents/InvoiceDocuments.vue
 @ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/invoic
es/MonoInvoice.vue
 @ ./src/components/invoices/MonoInvoice.vue
 @ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/invoic
es/InvoicesPage.vue
 @ ./src/components/invoices/InvoicesPage.vue
 @ ./src/app-router.js
 @ ./src/app-main.js
 @ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server babel-polyfill ./src/app-main.js
@zalishchuk

This comment has been minimized.

Copy link

commented Sep 14, 2017

Will this problem be solved and when will this happen?

@hzoo

This comment has been minimized.

Copy link
Member

commented Sep 14, 2017

Yeah it can be solved, it will happen when someone wants to look into it. If you are assuming someone on the team is going to do it, we might if we have time

Although I wouldn't of expected it to just be a syntax error since we include it in the parser options

"optionalChaining",

@zalishchuk

This comment has been minimized.

Copy link

commented Sep 15, 2017

@hzoo I do not know specifically what the root of the problem is, but as a fact, eslint expects to see the : (probably, a part of ternary operator) after ?.. Most likely, it tries to parse the expression as a ternary, not an optional chaining ?. operator. Maybe the problem lies in the eslint package itself?

@hzoo

This comment has been minimized.

Copy link
Member

commented Sep 15, 2017

@zalishchuk it doesn't make sense because babylon parses ?. with that plugin which is on if you use babel-eslint (maybe try the latest version), and make sure the error is coming from eslint, not something else?

@szeller

This comment has been minimized.

Copy link

commented Nov 27, 2017

I'm having the same issue with babel-eslint current and babel 7.0.0 beta using node v6.11.1 and npm v3.10.10

I went ahead and made as simple of test case as I could in this repo. https://github.com/szeller/test_chaining

If you pull the repo and run the following, you should get the same error

npm install
./node_modules/.bin/eslint index.js

results in

2:21  error  Parsing error: Unexpected token .
@szeller

This comment has been minimized.

Copy link

commented Dec 1, 2017

@hzoo @Akryum any updates or suggestions on this one?

@szeller

This comment has been minimized.

Copy link

commented Dec 2, 2017

nevermind 😞 I realized that when I was copying my original .eslintrc between repos and resolving conflicts, I lost the "parser": "babel-eslint" option. I should have noticed when I made the simple test repo ... oh well ...

@hzoo

This comment has been minimized.

Copy link
Member

commented Jan 8, 2018

Ok closing then?

@hzoo hzoo closed this Jan 8, 2018
@gabaum10

This comment has been minimized.

Copy link

commented Jun 14, 2018

@szeller this is an old issue, but did you figure out how to prevent the eslint parser from treating the optional result as undefined? For example:

    const test = {
        a: 'test string'
    };

    console.log(test.a);
    console.log(test.a?.b);

Eslint is complaining about b being undefined under the no-undef flag. Did you notice that as well?

@zalishchuk

This comment has been minimized.

Copy link

commented Jun 14, 2018

@gabaum10 try to install babel-eslint@8.2.1 and tell if that fixes your problem.

@gabaum10

This comment has been minimized.

Copy link

commented Jun 14, 2018

@zalishchuk I installed babel-eslint@8.2.3 and am still running into the issue.

@zalishchuk

This comment has been minimized.

Copy link

commented Jun 14, 2018

@gabaum10 try to install exact v8.2.1, it fixes the problem for me, because ^8.2.2 does not work for me.

@gabaum10

This comment has been minimized.

Copy link

commented Jun 14, 2018

Wow. That's hilarious, that did work. You da man @zalishchuk ! Is there an open issue for that? I can make one and link it if not.

@zalishchuk

This comment has been minimized.

Copy link

commented Jun 14, 2018

@gabaum10 I don't know if there is, but you can try.

@ryanpcmcquen

This comment has been minimized.

Copy link

commented Sep 4, 2018

This issue still exists in 9.0.0 ... how do we get it reopened?

@Eugeny

This comment has been minimized.

Copy link

commented Jan 23, 2019

Still not working with the optional call syntax:

this.onChange?.()

25:9 error Expected an assignment or function call and instead saw an expression no-unused-expressions

Babel itself compiles the code just fine

@ryanpcmcquen

This comment has been minimized.

Copy link

commented Jan 23, 2019

@Eugeny, wouldn't this be better?

this?.onChange()
@Eugeny

This comment has been minimized.

Copy link

commented Jan 23, 2019

It won't because it leads to ambiguity in this case: this.something?.onChange(), and also it's the way it is in the proposal.

@ryanpcmcquen

This comment has been minimized.

Copy link

commented Jan 23, 2019

@Eugeny, so the point of this is to call onChange only if it is a function?

this.onChange?.()
@caravinci

This comment has been minimized.

Copy link

commented Jan 23, 2019

@Eugeny, so the point of this is to call onChange only if it is a function?

this.onChange?.()

Even if that's the case, onChange? would only check whether there's an 'onChange', not its type. right?

@Eugeny

This comment has been minimized.

Copy link

commented Jan 23, 2019

Right

@caravinci

This comment has been minimized.

Copy link

commented Jan 23, 2019

So in theory, even as the proposal allows for conditional function/method call, there's a chance you would get a obj?.onChange?.() // "Uncaught TypeError: onChange is not a function" if typeof onChange != "function.
The only mention to that case I could find in the proposal is here, but they don't cover a proper response to the type mismatch. What should be the right approach then?

@0rvar

This comment has been minimized.

Copy link

commented Jan 23, 2019

So in theory, even as the proposal allows for conditional function/method call, there's a chance you would get a obj?.onChange?.() // "Uncaught TypeError: onChange is not a function" if typeof onChange != "function.

That is orthogonal to this syntax. To protect against type errors, use Flow or Typescript.

@Eugeny

This comment has been minimized.

Copy link

commented Jan 23, 2019

I don't see why this is a part of the conversation to be honest. The proposal basically suggests implementing

obj.onChange?.()

as

if (obj.onChange !== undefined && obj.onChange !== null) {
	obj.onChange()
}

It doesn't check types and it's not supposed to.

This is about babel-eslint reporting errors where there are none.

@ryanpcmcquen

This comment has been minimized.

Copy link

commented Jan 24, 2019

@Eugeny, that is not how it works. Compile it out with babel.

This code:

foo.onChange?.()

Compiles to:

var _foo$onChange, _foo;

(_foo$onChange = (_foo = foo).onChange) === null || _foo$onChange === void 0 ? void 0 : _foo$onChange.call(_foo);

While this code:

foo?.onChange()

Compiles to:

var _foo;

(_foo = foo) === null || _foo === void 0 ? void 0 : _foo.onChange();

So your intentional outcome results from putting the optional chaining operator before the onChange method (as I mentioned earlier).

@Eugeny

This comment has been minimized.

Copy link

commented Jan 24, 2019

Look, I have kind of lost track of where this conversation is going. Besides, your first compiled snippet is identical to mine.

@Eugeny

This comment has been minimized.

Copy link

commented Jan 24, 2019

By the way, I fixed my issue (which was an incorrect linter message) by installing @babel/plugin-proposal-nullish-coalescing-operator directly into my atom-linter-eslint's own node_modules (standalone babel-eslint turned out to be working fine).

@CaptainN

This comment has been minimized.

Copy link

commented May 2, 2019

This is still an error. What needs to changed to get it fixed?

@ryanpcmcquen

This comment has been minimized.

Copy link

commented May 2, 2019

@Eugeny, no, the second snippet is the identical one.

@yuritoledo

This comment has been minimized.

Copy link

commented Jul 2, 2019

Any updates?

@xgqfrms

This comment was marked as resolved.

Copy link

commented Sep 17, 2019

maybe a solution

not recommend ⚠️

  1. change .js to .ts
  2. modify .vscode/setting.json
{
  "typescript.validate.enable": false,
}

ts OK

image

js bug

image

@yuritoledo

This comment has been minimized.

Copy link

commented Sep 17, 2019

@xgqfrms i recommend do this only in JS files, because you don't have any lose.
However, in TS files, you lose the intellisense

@xgqfrms

This comment has been minimized.

Copy link

commented Sep 17, 2019

@yuritoledo yes, you are right.

@thinhvo0108

This comment has been minimized.

Copy link

commented Sep 19, 2019

nevermind 😞 I realized that when I was copying my original .eslintrc between repos and resolving conflicts, I lost the "parser": "babel-eslint" option. I should have noticed when I made the simple test repo ... oh well ...

Thanks @szeller
=> This works for me, but I think babel/plugin-proposal-optional-chaining should be installed as dev-dependency first

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.