Skip to content

Commit e955268

Browse files
committed
fix #4421: lower generated class fields if needed
1 parent a5a2500 commit e955268

4 files changed

Lines changed: 99 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@
22

33
## Unreleased
44

5+
* Fix lowering of define semantics for TypeScript parameter properties ([#4421](https://github.com/evanw/esbuild/issues/4421))
6+
7+
The previous release incorrectly generated class fields for TypeScript parameter properties even when the configured target environment does not support class fields. With this release, the generated class fields will now be correctly lowered in this case:
8+
9+
```ts
10+
// Original code
11+
class Foo {
12+
constructor(public x = 1) {}
13+
y = 2
14+
}
15+
16+
// Old output (with --loader=ts --target=es2021)
17+
class Foo {
18+
constructor(x = 1) {
19+
this.x = x;
20+
__publicField(this, "y", 2);
21+
}
22+
x;
23+
}
24+
25+
// New output (with --loader=ts --target=es2021)
26+
class Foo {
27+
constructor(x = 1) {
28+
__publicField(this, "x", x);
29+
__publicField(this, "y", 2);
30+
}
31+
}
32+
```
33+
534
* Update the Go compiler from 1.25.7 to 1.26.1
635

736
This upgrade should not affect anything. However, there have been some significant internal changes to the Go compiler, so esbuild could potentially behave differently in certain edge cases:

internal/bundler_tests/bundler_ts_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3036,3 +3036,31 @@ func TestParameterPropsUseDefineForClassFieldsFalse(t *testing.T) {
30363036
},
30373037
})
30383038
}
3039+
3040+
// See: https://github.com/evanw/esbuild/issues/4421
3041+
func TestParameterPropsUseDefineForClassFieldsTrueLowered(t *testing.T) {
3042+
ts_suite.expectBundled(t, bundled{
3043+
files: map[string]string{
3044+
"/entry.ts": `
3045+
class Foo {
3046+
static { console.log('a') }
3047+
a = 1
3048+
static { console.log('b') }
3049+
constructor(public b1 = 2.1, public b2 = 2.2) {
3050+
}
3051+
static { console.log('c') }
3052+
c = 3
3053+
}
3054+
`,
3055+
},
3056+
entryPaths: []string{"/entry.ts"},
3057+
options: config.Options{
3058+
Mode: config.ModeBundle,
3059+
AbsOutputFile: "/out.js",
3060+
TS: config.TSOptions{Config: config.TSConfig{
3061+
UseDefineForClassFields: config.True,
3062+
}},
3063+
UnsupportedJSFeatures: compat.ClassField,
3064+
},
3065+
})
3066+
}

internal/bundler_tests/snapshots/snapshots_ts.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,28 @@ var Foo = class {
161161
c = 3;
162162
};
163163

164+
================================================================================
165+
TestParameterPropsUseDefineForClassFieldsTrueLowered
166+
---------- /out.js ----------
167+
// entry.ts
168+
var Foo = class {
169+
constructor(b1 = 2.1, b2 = 2.2) {
170+
__publicField(this, "b1", b1);
171+
__publicField(this, "b2", b2);
172+
__publicField(this, "a", 1);
173+
__publicField(this, "c", 3);
174+
}
175+
static {
176+
console.log("a");
177+
}
178+
static {
179+
console.log("b");
180+
}
181+
static {
182+
console.log("c");
183+
}
184+
};
185+
164186
================================================================================
165187
TestTSAbstractClassFieldUseAssign
166188
---------- /out.js ----------

internal/js_parser/js_parser_lower_class.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,23 +1074,29 @@ func (ctx *lowerClassContext) lowerMethod(p *parser, prop js_ast.Property, priva
10741074
for _, arg := range ctx.ctor.Fn.Args {
10751075
if arg.IsTypeScriptCtorField {
10761076
if id, ok := arg.Binding.Data.(*js_ast.BIdentifier); ok {
1077+
loc := arg.Binding.Loc
10771078
name := p.symbols[id.Ref.InnerIndex].OriginalName
1078-
ctx.parameterFields = append(ctx.parameterFields, js_ast.AssignStmt(
1079-
js_ast.Expr{Loc: arg.Binding.Loc, Data: p.dotOrMangledPropVisit(
1080-
js_ast.Expr{Loc: arg.Binding.Loc, Data: js_ast.EThisShared},
1081-
name,
1082-
arg.Binding.Loc,
1083-
)},
1084-
js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}},
1085-
))
1079+
target := js_ast.Expr{Loc: loc, Data: js_ast.EThisShared}
1080+
init := js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: id.Ref}}
10861081

10871082
// See: https://github.com/evanw/esbuild/issues/4421
1088-
if ctx.class.UseDefineForClassFields {
1089-
ctx.parameterFieldProps = append(ctx.parameterFieldProps, js_ast.Property{
1090-
Kind: js_ast.PropertyField,
1091-
Loc: arg.Binding.Loc,
1092-
Key: js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}},
1093-
})
1083+
if !ctx.class.UseDefineForClassFields || !p.options.unsupportedJSFeatures.Has(compat.ClassField) {
1084+
ctx.parameterFields = append(ctx.parameterFields, js_ast.AssignStmt(
1085+
js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropVisit(target, name, loc)},
1086+
init,
1087+
))
1088+
} else if ctx.class.UseDefineForClassFields {
1089+
key := js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}}
1090+
if p.options.unsupportedJSFeatures.Has(compat.ClassField) {
1091+
ctx.parameterFields = append(ctx.parameterFields, js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{
1092+
Value: js_ast.Expr{Loc: loc, Data: &js_ast.ECall{
1093+
Target: p.importFromRuntime(loc, "__publicField"),
1094+
Args: []js_ast.Expr{target, key, init},
1095+
}},
1096+
}})
1097+
} else {
1098+
ctx.parameterFieldProps = append(ctx.parameterFieldProps, js_ast.Property{Kind: js_ast.PropertyField, Loc: loc, Key: key})
1099+
}
10941100
}
10951101
}
10961102
}

0 commit comments

Comments
 (0)