Skip to content

ESTree AST and Oxc AST type discrepancies #10139

@knightedcodemonkey

Description

@knightedcodemonkey

How to resolve the difference between ESTree loc of type SourceLocation and oxc Span type? For instance, when trying to walk an AST with estree-walker and narrowing the type to a Literal there is no way to access the start and end unless you use a type assertion to cast it as a StringLiteral.

First of all there is a discrepancy between an ESTree Program and an Oxc Program sourceType due to Oxc supporting 'unambiguous'.

Given this:

import type { Program as ESTreeProgram } from 'estree'
import { walk } from 'estree-walker'

walk (ast.program as ESTreeProgram, {
  enter(node) {
      if (node.type === 'ImportDeclaration') {
        if (node.source.type === 'Literal') {
          const source = node.source

          source.start // <---- type error here
        }
      }
  }
}

You'll get a type error:

Property 'start' does not exist on type 'Literal'.
  Property 'start' does not exist on type 'SimpleLiteral'.

Despite the AST node having this shape:

Node: {
  type: 'ImportDeclaration',
  start: 0,
  end: 37,
  specifiers: [
    {
      type: 'ImportDefaultSpecifier',
      start: 7,
      end: 10,
      local: [Object]
    }
  ],
  source: {
    type: 'Literal',
    start: 16,
    end: 37,
    value: './path/to/module.js',
    raw: "'./path/to/module.js'"
  },
  attributes: []
}

So the start property exists on the node, but Oxc has no SimpleLiteral type, instead it uses StringLiteral.

After using a type assertion the type error goes away:

if (node.source.type === 'Literal') {
  const source = node.source as StringLiteral // from '@oxc-project/types'

  source.start
}

If I try to use an Oxc Program as the original type to walk(ast.program as OxProgram) you'll instead be encountered with another type error:

    Types of property 'sourceType' are incompatible.
      Type 'ModuleKind' is not assignable to type '"script" | "module"'.
        Type '"unambiguous"' is not assignable to type '"script" | "module"'.

Finally, I tried this:

import { Program as OxcProgram } from '@oxc-project/types'
import { walk } from 'estree-walker'

interface Program extends OxcProgram {
  sourceType: 'module' | 'script'
}

// ast is ParseResult
walk(ast.program as Program, {})

Which results in the type error:

Argument of type 'Program' is not assignable to parameter of type 'Node'.
  Type 'Program' is not assignable to type ESTree.Program'.
    Types of property 'body' are incompatible.
      Type '(Directive | Statement)[]' is not assignable to type '(ModuleDeclaration | Statement | Directive)[]'.
        Type 'Directive | Statement' is not assignable to type 'ModuleDeclaration | Statement | Directive'.
          Type 'Directive' is not assignable to type 'ModuleDeclaration | Statement | Directive'.
            Type 'Directive' is not assignable to type 'ExpressionStatement | Directive'.
              Type Oxc.Directive' is not assignable to type ESTree.Directive'.
                Types of property 'expression' are incompatible.
                  Type 'StringLiteral' is not assignable to type 'Literal'.
                    Property 'bigint' is missing in type 'StringLiteral' but required in type 'BigIntLiteral'.

I haven't checked yet wether the actual AST from oxc-parser is compatible with the AST from ESTree, but the types don't seem to be.

How to properly walk a typed AST from oxc-parser using estree-walker?

Metadata

Metadata

Assignees

Labels

A-astArea - AST

Type

Priority

None yet

Effort

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions