## JavaScript syntax errors compared (2021)

A JavaScript error beauty contest

Written by strager on 2021-12-11

- Introduction
- Contestants
- Code samples
- Summary

### Introduction

Making mistakes is inevitable. Even JavaScript pros write syntax errors. The words unexpected token are familiar to junior and senior engineers alike.

How much time do programmers waste hunting down syntax errors? I don't know. Probably a lot. Luckily, modern compilers and editors try to help us fix our mistakes.

Let's compare various tools to see how well they communicate JavaScript syntax errors to programmers.

### Contestants

To automate the bug-finding process, let's use two kinds of JavaScript tools: tools you have to use (JS engines and transpilers) and static analysis tools designed to find bugs (linters). We'll test the following tools:

- Babel compiler (“Ba”) version v7.16.4
- ESLint with the Espree parser (“ESL”) version v8.4.1
- JavaScriptCore, used by Safari (“JSC”) version 278391
- quick-lint-js (“qljs”) version 0.7.1
- SpiderMonkey, used by Mozilla Firefox (“SM”) version 90.0b2
- TypeScript compiler (“TS”) version 4.5.3
- V8, used by Google Chrome and Node.js (“V8”) version 9.3.62

### Code samples

There are so many syntax errors to choose from! Instead of inventing code for demonstration, let's take JavaScript snippets from real programmers on Stack Overflow:

To compare tools, I rated each error message on a scale from 1 to 5:

1. The message suggests the correct solution.
2. The message suggests a technically correct solution. The suggestion is confusingly worded, in the wrong location, or leads to more problems.
3. The message explains the problem but does not suggest a solution.
4. The message explains a different problem or the problem in the wrong location.
5. The message suggests an incorrect solution or misleads the user, or there is no message reported at all.

#### `;` instead of `,`

Object literal properties are separated by commas (`,`). Semicolons (`;`) are not allowed. The following code uses a semicolon instead of a colon in an object literal:

    $('.view-content').hoverscroll({
        arrows: true,
        arrowsOpacity: 0.7;
    });

Asked by Joe on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Unexpected token, expected ","
ESL | Unexpected token ;
JSC | Unexpected token ';'. Expected '}' to end an object literal.
qljs | expected ',' between object literal entries
SM | missing } after property list note: { opened at line 1, column 31
TS | ',' expected.
V8 | Unexpected token ';'

Tip: Hover over the message on the right to show the error on the left.

5/5: Ba, qljs, and TS did a great job. They suggested a solution to the problem.

3/5: ESL and V8 reported the problem but did not suggest a solution.

1/5: JSC and SM suggested incorrect solutions.

#### Invalid object literal key

Object literal property names must be simple identifiers or quoted strings. The following code example tries to create a nested object by writing a dot (`.`) in the property name:

    var company = new Company({
      name: body.name,
      address: body.address,
      friends.name: body.friendName,
      statuses: { status: "New" },
    });

Asked by jcubic on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Unexpected token, expected ","
ESL | Unexpected token .
JSC | Unexpected token '.'. Expected a ':' following the property name 'friends'.
qljs | unexpected expression; missing key for object entry missing comma between object literal entries token not implemented in parse_object_literal: colon
SM | missing : after property id
TS | ',' expected. ',' expected. ',' expected.
V8 | Unexpected token '.'

Tip: Hover over the tool names below to focus on the errors above.

None of the tools suggested the correct solution to the problem.

4/5: SM reported the problem and suggested a possible solution. However, the suggestion would lead to more syntax errors.

3/5: JSC reported the problem and suggested a possible solution. However, the message is ambiguous because it reports only a line number and because the line contains two '.' characters.

3/5: ESL and V8 reported the problem but did not suggest a solution.

1/5: Ba, qljs, and TS and suggested incorrect solutions. qljs also crashed.

#### Newline after `return`

If `return` is immediately followed by a newline character, `undefined` is returned, and the following code is interpreted as more statements. The following code expects the `return` statement to return an object literal:

    var homeModelTemplate = function(){
        return
        {
            fromDateSearch: new Date(),
            toDateSearch: new Date()
        };
    };

Asked anonymously on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Missing semicolon.
ESL | Unexpected token :
JSC | Unexpected token ':'. Parse error.
qljs | return statement returns nothing (undefined) missing semicolon after statement unexpected token use of undeclared variable: toDateSearch
SM | unexpected token: ':'
TS | ';' expected.
V8 | Unexpected token ':'

5/5: qljs did a great job. It reported the real problem and hinted at the solution. qljs also reported unrelated errors.

2/5: ESL, JSC, SM, and V8 reported a symptom of the problem, but did not guide the programmer toward a fix.

1/5: Ba and TS suggested incorrect solutions.

#### Missing `( )` around parameter

If an arrow function has a single parameter, and that parameter is an object destructuring, parentheses are required around the parameter. The following code omits the parentheses:

    colls.map({id, ...other} => {
      return preview({
        key: id,
        ...other
      });
    })

Asked by Emile Bergeron
on Stack Overflow
(CC BY-SA 4.0)

Tool | Error message
--- | ---
Ba | Unexpected token, expected ","
ESL | Unexpected token =>
JSC | Unexpected token '=>'. Expected ')' to end an argument list.
qljs | (no errors)
SM | invalid arrow-function arguments (parentheses around the arrow-function may help)
TS | ',' expected. ':' expected. ',' expected.
V8 | Malformed arrow function parameter list

5/5: SM did a great job. It suggested a solution to the problem.

4/5: JSC and V8 hinted at a solution to the problem, but the message isn't as clear as SM's.

3/5: ESL reported the problem but did not suggest a solution.

1/5: Ba and TS suggested incorrect solutions. TS also reported unrelated errors.

1/5: qljs reported no error at all.

#### Keyword variable name

With a few exceptions for legacy reasons, function parameters cannot be named a keyword. The following code tries to name a function parameter `class`, which is a keyword:

    function Classes(class, sched) {
      this.class = class;
      this.scheduled = sched;
    }

Asked by anonymous on Stack Overflow
(CC BY-SA 4.0)

Tool | Error message
--- | ---
Ba | Unexpected keyword 'class'.
ESL | Unexpected keyword 'class'
JSC | Cannot use the keyword 'class' as a parameter name.
qljs | token not implemented in parse_and_visit_function_parameters: kw_class
SM | missing formal parameter
TS | 'class' is not allowed as a parameter name. '{' expected. Unexpected keyword or identifier. Declaration or statement expected. '{' expected.
V8 | Unexpected token 'class'

5/5: JSC and TS clearly reported the problem and hinted at a solution. TS also reported unrelated errors.

3/5: Ba, ESL, qljs, and V8 reported the problem but did not suggest a solution. qljs also crashed.

1/5: SM suggested an incorrect solution.

#### Missing `||` between expressions

Expressions can't be next to each other without an operator in between. The following code is missing the logical or operator (`||`) between two expressions in the `while` loop:

    while( s2[Y]=="#" || s2[Y+1] =="X" s3[Y]=="#" || s3[Y+1] =="X");

Asked by bdukes on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Unexpected token, expected ")"
ESL | Unexpected token s3
JSC | Unexpected identifier 's3'. Expected ')' to end a while loop condition.
qljs | while loop is missing ')' around condition unmatched parenthesis
SM | missing ) after condition
TS | ')' expected. ';' expected.
V8 | Unexpected identifier

None of the tools suggested the correct solution to the problem.

3/5: ESL and V8 reported the problem but did not suggest a solution.

1/5: Ba, JSC, qljs, SM, and TS suggested incorrect solutions to the problem.

#### `elseif` instead of `else if`

`elseif` is a keyword in some languages, but not in JavaScript. The correct code is `else if` (two words). The following code uses `elseif` by mistake:

    var car;
    if(input.val() == "Lamborghini") {
        car = 389;
    }elseif(input.val() == "Ferrari"){
        car = 349;
    }else{
        car = 0;
    }

Asked by TooCooL on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Missing semicolon.
ESL | Unexpected token {
JSC | Unexpected token '{'
qljs | missing semicolon after statement 'else' has no corresponding 'if'
SM | unexpected token: '{'
TS | ';' expected. Declaration or statement expected.
V8 | Unexpected token '{'

3/5: qljs reported that the `if` keyword was missing or misplaced. However, qljs also suggested an incorrect solution to the problem.

2/5: ESL, JSC, SM, and V8 reported an unrelated problem but did not suggest a solution.

1/5: Ba and TS suggested incorrect solutions to the problem.

#### Missing `,` between arguments

Function calls have a comma (`,`)-separated arguments. The following code forgets a comma between two parameters:

    $.ajax({
      url: "loadcontent1.php",
      data: {
        lastid: 'postitem',
      }
      success: function(html) {
        $("#content").append(html);
      }
    });

Asked by Felix Kling on Stack Overflow
(CC BY-SA 3.0)

Tool | Error message
--- | ---
Ba | Unexpected token, expected ","
ESL | Unexpected token success
JSC | Unexpected identifier 'success'. Expected '}' to end an object literal.
qljs | missing comma between object literal entries
SM | missing } after property list
TS | ',' expected.
V8 | Unexpected identifier

5/5: qljs did a great job. It suggested a solution to the problem.

4/5: Ba and TS also suggested a solution to the problem, but at a worse location than qljs.

3/5: ESL and V8 reported the problem but did not suggest a solution.

1/5: JSC and SM suggested incorrect solutions.

### Summary

Tool scores (1-5) for code samples
Code sample | Ba | ESL | JSC | qljs | SM | TS | V8 | avg
--- | --- | --- | --- | --- | --- | --- | --- | ---
`;` instead of `,` | 5 | 3 | 1 | 5 | 1 | 5 | 3 | 3.3
Invalid object literal key | 1 | 3 | 3 | 1 | 4 | 1 | 3 | 2.3
Newline after `return` | 1 | 2 | 2 | 5 | 2 | 1 | 2 | 2.1
Missing `( )` around parameter | 1 | 3 | 4 | 1 | 5 | 1 | 4 | 2.7
Keyword variable name | 3 | 3 | 5 | 3 | 1 | 5 | 3 | 3.3
Missing `||` between expressions | 1 | 3 | 1 | 1 | 1 | 1 | 3 | 1.6
`elseif` instead of `else if` | 1 | 2 | 2 | 3 | 2 | 1 | 2 | 1.9
Missing `,` between arguments | 4 | 3 | 1 | 5 | 1 | 4 | 3 | 3.0
(total) | 17 | 22 | 19 | 24 | 17 | 19 | 23 |
| Code sample | Ba | ESL | JSC | qljs | SM | TS | V8 | avg

No tool was amazing at finding bugs in all of our code samples. To get the best error experience, use a mix of tools, not just one tool.

ESL, qljs, and V8 stand out as good tools for debugging syntax errors. ESL and V8 consistently do a decent job, never scoring 1 in any code sample.

qljs and TS are volatile, doing a great job (5/5) or a terrible job (1/5) depending on the error in question.

Despite being tied for the worst tool overall, SM beat all other tools in two code samples.

qljs, TS, and V8 report good-quality error spans. Ba, ESL, and SM report only a single line-column location, not a span. JSC only reports line numbers, making some messages ambiguous (such as in invalid object literal key).

Most tools did a poor job in two code samples, missing `||` between expressions and `elseif` instead of `else if`. Sometimes, broken code is hard to understand for computers.

qljs and TS try to recover after errors. Recovering is helpful in an editor, but sometimes it leads to confusing reports. All other tools stop at the first error.