(From https://github.com/eslint/eslint/issues/9124)
Tell us about your environment
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
Edits by @JLHwung
The optional chaining support is now tracked in https://github.com/babel/babel/issues/11711.
Will this problem be solved and when will this happen?
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 https://github.com/babel/babel-eslint/blob/905887c4fe4e5f4b56e34acb526e80aad83880cb/index.js#L413
@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?
@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?
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 .
@hzoo @Akryum any updates or suggestions on this one?
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 ...
Ok closing then?
@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?
@gabaum10 try to install [email protected] and tell if that fixes your problem.
@zalishchuk I installed [email protected] and am still running into the issue.
@gabaum10 try to install exact v8.2.1, it fixes the problem for me, because ^8.2.2 does not work for me.
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.
@gabaum10 I don't know if there is, but you can try.
This issue still exists in 9.0.0 ... how do we get it reopened?
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
@Eugeny, wouldn't this be better?
this?.onChange()
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.
@Eugeny, so the point of this is to call onChange only if it is a function?
this.onChange?.()
@Eugeny, so the point of this is to call
onChangeonly 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?
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?
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"iftypeof onChange != "function.
That is orthogonal to this syntax. To protect against type errors, use Flow or Typescript.
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.
@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).
Look, I have kind of lost track of where this conversation is going. Besides, your first compiled snippet is identical to mine.
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).
This is still an error. What needs to changed to get it fixed?
@Eugeny, no, the second snippet is the identical one.
Any updates?
not recommend 鈿狅笍
.js to .ts.vscode/setting.json{
"typescript.validate.enable": false,
}


@xgqfrms i recommend do this only in JS files, because you don't have any lose.
However, in TS files, you lose the intellisense
@yuritoledo yes, you are right.
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
updates on this?
JavaScript and TypeScript Nightly
https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next

Any update?
Any update?
shitT!!!
Any update?
babel-eslint has been moved to the Babel repo. We are tracking the optional chaining support in https://github.com/babel/babel/issues/11711.
Actually, I've found a solution installing babel-eslint and eslint-plugin-babel as dev-dependencies running npm install -D babel-eslint eslint-plugin-babel and adding this to .eslintrc.json
{ ...,
"parser": "babel-eslint",
"plugins": [
"babel"
], ...}
This have worked for me on a React Native project after trying many other solutions to use eslint properly with optional chaining operator. I hope it could be useful for someone.
Most helpful comment
I don't see why this is a part of the conversation to be honest. The proposal basically suggests implementing
as
It doesn't check types and it's not supposed to.
This is about
babel-eslintreporting errors where there are none.