Skip to content

Commit e755189

Browse files
committed
ts: forbid regular decorators on declare fields
1 parent 69c9e7f commit e755189

3 files changed

Lines changed: 24 additions & 4 deletions

File tree

internal/bundler_tests/bundler_ts_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -990,14 +990,12 @@ func TestTSExperimentalDecoratorsNoConfig(t *testing.T) {
990990
@x @y mUndef: any
991991
@x @y mDef = 1
992992
@x @y method() { return new Foo }
993-
@x @y declare mDecl: any
994993
@x @y accessor aUndef: any
995994
@x @y accessor aDef = 1
996995
997996
@x @y static sUndef: any
998997
@x @y static sDef = new Foo
999998
@x @y static sMethod() { return new Foo }
1000-
@x @y static declare sDecl: any
1001999
@x @y static accessor asUndef: any
10021000
@x @y static accessor asDef = 1
10031001

internal/js_parser/js_parser.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,10 +2181,11 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
21812181
scopeIndex := len(p.scopesInOrder)
21822182

21832183
if prop, ok := p.parseProperty(startLoc, kind, opts, nil); ok &&
2184-
prop.Kind == js_ast.PropertyNormal && prop.ValueOrNil.Data == nil && len(opts.decorators) > 0 {
2184+
prop.Kind == js_ast.PropertyNormal && prop.ValueOrNil.Data == nil &&
2185+
(p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0) {
21852186
// If this is a well-formed class field with the "declare" keyword,
21862187
// only keep the declaration to preserve its side-effects when
2187-
// there are TypeScript decorators present:
2188+
// there are TypeScript experimental decorators present:
21882189
//
21892190
// class Foo {
21902191
// // Remove this
@@ -2194,6 +2195,15 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
21942195
// @decorator(console.log('side effect 2')) declare bar
21952196
// }
21962197
//
2198+
// This behavior is surprisingly somehow valid with TypeScript
2199+
// experimental decorators, which was possibly by accident.
2200+
// TypeScript does not allow this with JavaScript decorators.
2201+
//
2202+
// References:
2203+
//
2204+
// https://github.com/evanw/esbuild/issues/1675
2205+
// https://github.com/microsoft/TypeScript/issues/46345
2206+
//
21972207
prop.Kind = js_ast.PropertyDeclare
21982208
return prop, true
21992209
}
@@ -6241,6 +6251,7 @@ func (p *parser) parseClass(classKeyword logger.Range, name *ast.LocRef, classOp
62416251

62426252
// Parse decorators for this property
62436253
firstDecoratorLoc := p.lexer.Loc()
6254+
scopeIndex := len(p.scopesInOrder)
62446255
opts.decorators = p.parseDecorators(p.currentScope, classKeyword, opts.decoratorContext)
62456256

62466257
// This property may turn out to be a type in TypeScript, which should be ignored
@@ -6261,6 +6272,9 @@ func (p *parser) parseClass(classKeyword logger.Range, name *ast.LocRef, classOp
62616272
hasConstructor = true
62626273
}
62636274
}
6275+
} else if !classOpts.isTypeScriptDeclare && len(opts.decorators) > 0 {
6276+
p.log.AddError(&p.tracker, logger.Range{Loc: firstDecoratorLoc, Len: 1}, "Decorators are not valid here")
6277+
p.discardScopesUpTo(scopeIndex)
62646278
}
62656279
}
62666280

internal/js_parser/ts_parser_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,10 @@ func TestTSExperimentalDecorator(t *testing.T) {
20292029
expectParseErrorExperimentalDecoratorTS(t, "@x export @y class Foo {}", "<stdin>: ERROR: Decorators are not valid here\n")
20302030
expectParseErrorExperimentalDecoratorTS(t, "@x export default abstract", "<stdin>: ERROR: Decorators are not valid here\n")
20312031
expectParseErrorExperimentalDecoratorTS(t, "@x export @y default class {}", "<stdin>: ERROR: Decorators are not valid here\n<stdin>: ERROR: Unexpected \"default\"\n")
2032+
2033+
// TypeScript experimental decorators are actually allowed on declared fields
2034+
expectPrintedExperimentalDecoratorTS(t, "class Foo { @(() => {}) declare foo: any; @(() => {}) bar: any }",
2035+
"class Foo {\n bar;\n}\n__decorateClass([\n () => {\n }\n], Foo.prototype, \"foo\", 2);\n__decorateClass([\n () => {\n }\n], Foo.prototype, \"bar\", 2);\n")
20322036
}
20332037

20342038
func TestTSDecorators(t *testing.T) {
@@ -2104,6 +2108,10 @@ func TestTSDecorators(t *testing.T) {
21042108
expectParseErrorTS(t, "@x export @y class Foo {}", "<stdin>: ERROR: Decorators are not valid here\n")
21052109
expectParseErrorTS(t, "@x export default abstract", "<stdin>: ERROR: Decorators are not valid here\n")
21062110
expectParseErrorTS(t, "@x export @y default class {}", "<stdin>: ERROR: Decorators are not valid here\n<stdin>: ERROR: Unexpected \"default\"\n")
2111+
2112+
// JavaScript decorators are not allowed on declared fields
2113+
expectParseErrorTS(t, "class Foo { @(() => {}) declare foo: any; @(() => {}) bar: any }",
2114+
"<stdin>: ERROR: Decorators are not valid here\n")
21072115
}
21082116

21092117
func TestTSTry(t *testing.T) {

0 commit comments

Comments
 (0)