Skip to content

[Bug]: parser(ts), TSInstantiationExpression and optional ChainExpression #14613

@armano2

Description

@armano2

💻

  • Would you like to work on a fix?

How are you using Babel?

Programmatic API (babel.transform, babel.parse)

Input code

incorrectly generated ast:

a?.b<c>;

correctly generated ast

a<c>; 
a<c>();

full test sample:

a<b>;

(a<b>)<c>;
(a<b>)<c>();
(a<b>)<c>?.();
(a?.b<c>)<d>();
new (a<b>)<c>();

Configuration file name

No response

Configuration

  require('@babel/parser').parse(text, {
    sourceType: 'unambiguous',
    allowImportExportEverywhere: true,
    allowReturnOutsideFunction: true,
    ranges: true,
    plugins: [
      [ 'estree', { classFeatures: true }, ],
      'decorators-legacy',
      'classStaticBlock',
      'importAssertions',
      'typescript',
    ],
  });

Current and expected behavior

TSInstantiationExpression should define typeParameters and structure should be different


expected:
TSInstantiationExpression > { typeParameters: *, expression: ChainExpression }

received:
ChainExpression > { typeParameters: *, expression: TSInstantiationExpression }


From what I can tell issue is present only if optional chain expression is used, ? in a?.b<c>;

looks like issue is present only when converting nodes to estree (to be verified)


Expected AST
{
  "body": [
    {
      "expression": {
        "arguments": [],
        "callee": {
          "expression": {
            "expression": {
              "computed": false,
              "loc": {
                "end": { "column": 5, "line": 1 },
                "start": { "column": 1, "line": 1 }
              },
              "object": {
                "loc": {
                  "end": { "column": 2, "line": 1 },
                  "start": { "column": 1, "line": 1 }
                },
                "name": "a",
                "range": [1, 2],
                "type": "Identifier"
              },
              "optional": true,
              "property": {
                "loc": {
                  "end": { "column": 5, "line": 1 },
                  "start": { "column": 4, "line": 1 }
                },
                "name": "b",
                "range": [4, 5],
                "type": "Identifier"
              },
              "range": [1, 5],
              "type": "MemberExpression"
            },
            "loc": {
              "end": { "column": 5, "line": 1 },
              "start": { "column": 1, "line": 1 }
            },
            "range": [1, 5],
            "type": "ChainExpression"
          },
          "loc": {
            "end": { "column": 8, "line": 1 },
            "start": { "column": 1, "line": 1 }
          },
          "range": [1, 8],
          "type": "TSInstantiationExpression",
          "typeParameters": {
            "loc": {
              "end": { "column": 8, "line": 1 },
              "start": { "column": 5, "line": 1 }
            },
            "params": [
              {
                "loc": {
                  "end": { "column": 7, "line": 1 },
                  "start": { "column": 6, "line": 1 }
                },
                "range": [6, 7],
                "type": "TSTypeReference",
                "typeName": {
                  "loc": {
                    "end": { "column": 7, "line": 1 },
                    "start": { "column": 6, "line": 1 }
                  },
                  "name": "c",
                  "range": [6, 7],
                  "type": "Identifier"
                }
              }
            ],
            "range": [5, 8],
            "type": "TSTypeParameterInstantiation"
          }
        },
        "loc": {
          "end": { "column": 14, "line": 1 },
          "start": { "column": 0, "line": 1 }
        },
        "optional": false,
        "range": [0, 14],
        "type": "CallExpression",
        "typeParameters": {
          "loc": {
            "end": { "column": 12, "line": 1 },
            "start": { "column": 9, "line": 1 }
          },
          "params": [
            {
              "loc": {
                "end": { "column": 11, "line": 1 },
                "start": { "column": 10, "line": 1 }
              },
              "range": [10, 11],
              "type": "TSTypeReference",
              "typeName": {
                "loc": {
                  "end": { "column": 11, "line": 1 },
                  "start": { "column": 10, "line": 1 }
                },
                "name": "d",
                "range": [10, 11],
                "type": "Identifier"
              }
            }
          ],
          "range": [9, 12],
          "type": "TSTypeParameterInstantiation"
        }
      },
      "loc": {
        "end": { "column": 15, "line": 1 },
        "start": { "column": 0, "line": 1 }
      },
      "range": [0, 15],
      "type": "ExpressionStatement"
    }
  ],
  "sourceType": "script",
  "type": "Program"
}
Received AST
{
  "body": [
    {
      "expression": {
        "arguments": [],
        "callee": {
          "expression": {
            "expression": {
              "computed": false,
              "loc": {
                "end": { "column": 5, "line": 1 },
                "start": { "column": 1, "line": 1 }
              },
              "object": {
                "loc": {
                  "end": { "column": 2, "line": 1 },
                  "start": { "column": 1, "line": 1 }
                },
                "name": "a",
                "range": [1, 2],
                "type": "Identifier"
              },
              "optional": true,
              "property": {
                "loc": {
                  "end": { "column": 5, "line": 1 },
                  "start": { "column": 4, "line": 1 }
                },
                "name": "b",
                "range": [4, 5],
                "type": "Identifier"
              },
              "range": [1, 5],
              "type": "MemberExpression"
            },
            "loc": {
              "end": { "column": 8, "line": 1 },
              "start": { "column": 1, "line": 1 }
            },
            "range": [1, 8],
            "type": "TSInstantiationExpression",
            "typeParameters": {
              "loc": {
                "end": { "column": 8, "line": 1 },
                "start": { "column": 5, "line": 1 }
              },
              "params": [
                {
                  "loc": {
                    "end": { "column": 7, "line": 1 },
                    "start": { "column": 6, "line": 1 }
                  },
                  "range": [6, 7],
                  "type": "TSTypeReference",
                  "typeName": {
                    "loc": {
                      "end": { "column": 7, "line": 1 },
                      "start": { "column": 6, "line": 1 }
                    },
                    "name": "c",
                    "range": [6, 7],
                    "type": "Identifier"
                  }
                }
              ],
              "range": [5, 8],
              "type": "TSTypeParameterInstantiation"
            }
          },
          "loc": {
            "end": { "column": 8, "line": 1 },
            "start": { "column": 1, "line": 1 }
          },
          "range": [1, 8],
          "type": "ChainExpression"
        },
        "loc": {
          "end": { "column": 14, "line": 1 },
          "start": { "column": 0, "line": 1 }
        },
        "optional": false,
        "range": [0, 14],
        "type": "CallExpression",
        "typeParameters": {
          "loc": {
            "end": { "column": 12, "line": 1 },
            "start": { "column": 9, "line": 1 }
          },
          "params": [
            {
              "loc": {
                "end": { "column": 11, "line": 1 },
                "start": { "column": 10, "line": 1 }
              },
              "range": [10, 11],
              "type": "TSTypeReference",
              "typeName": {
                "loc": {
                  "end": { "column": 11, "line": 1 },
                  "start": { "column": 10, "line": 1 }
                },
                "name": "d",
                "range": [10, 11],
                "type": "Identifier"
              }
            }
          ],
          "range": [9, 12],
          "type": "TSTypeParameterInstantiation"
        }
      },
      "loc": {
        "end": { "column": 15, "line": 1 },
        "start": { "column": 0, "line": 1 }
      },
      "range": [0, 15],
      "type": "ExpressionStatement"
    }
  ],
  "sourceType": "script",
  "type": "Program"
}

astexplorer has to old version of babel to show it, but you can see expected result for ts-eslint

typescript-eslint.io/play

Environment

  • Babel version: ^7.18.2

Additional context

I'm not fully confident that ts-estree produce fully correct structure here but typeParameters should be located on TSInstantiationExpression instead of ChainExpression

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions