oxc-project / oxc-project/backlog
AST: tighten TSImportType to only valid options
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 7
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Replace TSImportType.options: Option<Box<ObjectExpression>> with a grammar-specific representation.
Import-type options are not arbitrary object expressions. Valid options have a constrained shape:
type Data = import("./data.json", {
with: {
type: "json",
},
});
The current AST allows builders and transforms to construct impossible forms such as:
import("x", {});
import("x", { other: {} });
import("x", { with: expression });
import("x", { with: { type: 1 } });
import("x", { with: { ...attributes } });
Motivation
Oxc currently represents the options as:
pub struct TSImportType<'a> {
pub source: StringLiteral<'a>,
pub options: Option<Box<'a, ObjectExpression<'a>>>,
pub qualifier: Option<TSImportTypeQualifier<'a>>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
ObjectExpression admits arbitrary properties, spreads, computed keys, methods, accessors, and arbitrary expression values. None of those accurately describe TypeScript import-type options.
The parser validates some of these restrictions, but invalid input is still recovered into the unrestricted AST shape. Direct AST construction can bypass the parser entirely. Every consumer must therefore handle structures which cannot occur in valid TypeScript.
TypeScript itself models this syntax as import attributes rather than a general object literal.
Current AST Shape
pub struct TSImportType<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub source: StringLiteral<'a>,
pub options: Option<Box<'a, ObjectExpression<'a>>>,
pub qualifier: Option<TSImportTypeQualifier<'a>>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
Proposed AST Shape
Introduce a dedicated options node containing a recognized keyword and statically shaped import attributes:
pub struct TSImportType<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub source: StringLiteral<'a>,
pub options: Option<Box<'a, TSImportTypeOptions<'a>>>,
pub qualifier: Option<TSImportTypeQualifier<'a>>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
pub struct TSImportTypeOptions<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub keyword: WithClauseKeyword,
pub attributes: Vec<'a, ImportAttribute<'a>>,
}
ImportAttribute already restricts keys to identifiers or string literals and values to string literals:
pub struct ImportAttribute<'a> {
pub key: ImportAttributeKey<'a>,
pub value: StringLiteral<'a>,
}
Reusing WithClause may also be possible, although a dedicated node may better reflect the additional { with: ... } wrapper used by import types.
The legacy assert spelling can remain representable through WithClauseKeyword if required for recovery or compatibility, without permitting arbitrary property names.
ESTree serialization should continue producing the existing ObjectExpression | null representation.
Syntax Mapping
| Source form | Current representation | Proposed representation |
|---|---|---|
import("x") |
options: None |
Unchanged |
import("x", { with: { type: "json" } }) |
Arbitrary nested ObjectExpression |
TSImportTypeOptions containing one ImportAttribute |
import("x", { assert: { type: "json" } }) |
Arbitrary nested ObjectExpression |
Recognized legacy keyword, if retained |
import("x", { other: {} }) |
Valid AST shape plus diagnostic | Diagnostic; arbitrary key cannot inhabit TSImportTypeOptions |
import("x", { with: value }) |
Any Expression |
Diagnostic; no arbitrary expression in the options AST |
import("x", { with: { type: 1 } }) |
Numeric expression value | Diagnostic; attribute values are string literals |
import("x", { with: { ...attrs } }) |
SpreadElement |
Diagnostic; spread cannot inhabit the dedicated node |
Guaranteed Invariants
TSImportType.optionscannot contain a general-purpose object expression.- Present options contain exactly one recognized outer keyword.
- Attribute keys are static identifiers or string literals.
- Attribute values are string literals.
- Spreads, computed keys, methods, accessors, and unrelated outer properties are unrepresentable.
- Parser recovery emits diagnostics without polluting the valid AST model.
- Existing ESTree output remains compatible.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the TSImportType definition and trace the parser's recovery path for import-type options, then inspect WithClause, ImportAttribute, and ESTree serialization. Confirm how the proposed dedicated options node fits existing AST conventions and compatibility requirements. Done means invalid option shapes are diagnosed without entering the valid AST model, while existing ESTree output remains compatible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100