mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
a33788e13e
## Summary
- Groups `Class::super_class` and `Class::super_type_arguments` into `Class::heritage`.
- Introduces `ClassHeritage` containing the superclass expression and its optional TypeScript type arguments.
- Makes superclass type arguments without a superclass expression unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub super_class: Option<Expression<'a>>,
#[ts]
pub super_type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
```
to:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub heritage: Option<ClassHeritage<'a>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub struct ClassHeritage<'a> {
pub expression: Expression<'a>,
#[ts]
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Why?
ECMAScript defines the complete `extends` clause as `ClassHeritage`:
```text
ClassHeritage : extends LeftHandSideExpression
```
TypeScript additionally permits type arguments on the heritage expression:
```ts
class Derived extends Base<T> {}
```
Previously, the AST stored the expression and type arguments independently:
```text
Class {
super_class: Some(Base),
super_type_arguments: Some(<T>),
...
}
```
This allowed an invalid state to be constructed:
```text
Class {
super_class: None,
super_type_arguments: Some(<T>),
...
}
```
Superclass type arguments cannot exist without an `extends` expression.
The parser already treated these fields as a pair. It parsed a class heritage entry and then split its expression and type arguments into two independent fields. Builders and transforms therefore had to preserve their relationship manually.
Grouping them into `ClassHeritage` makes the syntactic dependency explicit and prevents consumers from observing or constructing contradictory class heritage.
## Class heritage forms
A class without an `extends` clause has no heritage:
```js
class Base {}
```
```text
Class {
heritage: None,
...
}
```
A class extending an expression has heritage without type arguments:
```js
class Derived extends Base {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: None,
}),
...
}
```
TypeScript type arguments belong to the same heritage structure:
```ts
class Derived extends Base<T> {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: Some(<T>),
}),
...
}
```
The heritage expression may be any valid class heritage expression:
```js
class Derived extends mixin(Base) {}
class NullDerived extends null {}
```
## How to migrate
Code that previously accessed the superclass expression directly:
```rust
if let Some(super_class) = &class.super_class {
// ...
}
```
can use the compatibility accessor:
```rust
if let Some(super_class) = class.super_class() {
// ...
}
```
Code that needs both parts should access the grouped heritage:
```rust
if let Some(heritage) = &class.heritage {
let expression = &heritage.expression;
let type_arguments = heritage.type_arguments.as_deref();
}
```
Mutating consumers should update the nested fields:
```rust
if let Some(heritage) = &mut class.heritage {
heritage.type_arguments = None;
}
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `class.super_class` | `class.heritage.as_ref().map(\|h\| &h.expression)` or `class.super_class()` |
| `class.super_type_arguments` | `class.heritage.as_ref()?.type_arguments` or `class.super_type_arguments()` |
| `class.super_class.is_some()` | `class.heritage.is_some()` |
| Mutate `class.super_class` | Mutate `class.heritage.expression` |
| Clear `class.super_type_arguments` | Clear `class.heritage.type_arguments` |
| Pass superclass and type arguments separately to a `Class` builder | Construct and pass `ClassHeritage` |
| Clone both fields independently | Clone `class.heritage` |
```ts
superClass: Expression | null;
superTypeArguments?: TSTypeParameterInstantiation | null;
```
No `heritage` property or `ClassHeritage` node is added to the ESTree output.
fixes https://github.com/oxc-project/backlog/issues/216
77 lines
1.7 KiB
YAML
77 lines
1.7 KiB
YAML
checker.ts:
|
|
file size: 2922154 # 2.92 MB
|
|
sys allocs: 46
|
|
sys reallocs: 0
|
|
sys deallocs: 46
|
|
sys alloc bytes: 3866958 # 3.87 MB
|
|
sys peak growth: 3858362 # 3.86 MB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 12923744 # 12.92 MB
|
|
|
|
App.tsx:
|
|
file size: 415342 # 415.34 kB
|
|
sys allocs: 7
|
|
sys reallocs: 0
|
|
sys deallocs: 7
|
|
sys alloc bytes: 360848 # 360.85 kB
|
|
sys peak growth: 360848 # 360.85 kB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 2230856 # 2.23 MB
|
|
|
|
RadixUIAdoptionSection.jsx:
|
|
file size: 2520 # 2.52 kB
|
|
sys allocs: 7
|
|
sys reallocs: 0
|
|
sys deallocs: 7
|
|
sys alloc bytes: 2748 # 2.75 kB
|
|
sys peak growth: 2748 # 2.75 kB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 21200 # 21.20 kB
|
|
|
|
pdf.mjs:
|
|
file size: 567296 # 567.30 kB
|
|
sys allocs: 12
|
|
sys reallocs: 0
|
|
sys deallocs: 12
|
|
sys alloc bytes: 833710 # 833.71 kB
|
|
sys peak growth: 832762 # 832.76 kB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 4536712 # 4.54 MB
|
|
|
|
antd.js:
|
|
file size: 6686316 # 6.69 MB
|
|
sys allocs: 24
|
|
sys reallocs: 0
|
|
sys deallocs: 24
|
|
sys alloc bytes: 5512934 # 5.51 MB
|
|
sys peak growth: 5441278 # 5.44 MB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 29421920 # 29.42 MB
|
|
|
|
binder.ts:
|
|
file size: 193077 # 193.08 kB
|
|
sys allocs: 14
|
|
sys reallocs: 0
|
|
sys deallocs: 14
|
|
sys alloc bytes: 247026 # 247.03 kB
|
|
sys peak growth: 246886 # 246.89 kB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 916328 # 916.33 kB
|
|
|
|
kitchen-sink.tsx:
|
|
file size: 732898 # 732.90 kB
|
|
sys allocs: 33
|
|
sys reallocs: 0
|
|
sys deallocs: 33
|
|
sys alloc bytes: 1103942 # 1.10 MB
|
|
sys peak growth: 1101954 # 1.10 MB
|
|
arena allocs: 0
|
|
arena reallocs: 0
|
|
arena size: 6440104 # 6.44 MB
|