Skip to content

Commit 300eeb7

Browse files
committed
ts: allow non-null assertions in js decorators
1 parent 4d997d9 commit 300eeb7

File tree

3 files changed

+59
-38
lines changed

3 files changed

+59
-38
lines changed

internal/js_parser/js_parser.go

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6713,50 +6713,60 @@ func (p *parser) parseDecorator() js_ast.Expr {
67136713

67146714
memberExpr := js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(name)}}
67156715

6716-
// "@x<y>() class{}"
6717-
if p.options.ts.Parse {
6718-
p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{})
6719-
}
6720-
6721-
for p.lexer.Token == js_lexer.TDot {
6722-
p.lexer.Next()
6723-
6724-
if p.lexer.Token == js_lexer.TPrivateIdentifier {
6725-
name := p.lexer.Identifier
6726-
memberExpr.Data = &js_ast.EIndex{
6727-
Target: memberExpr,
6728-
Index: js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EPrivateIdentifier{Ref: p.storeNameInRef(name)}},
6716+
loop:
6717+
for {
6718+
switch p.lexer.Token {
6719+
case js_lexer.TExclamation:
6720+
// Skip over TypeScript non-null assertions
6721+
if p.lexer.HasNewlineBefore {
6722+
break loop
6723+
}
6724+
if !p.options.ts.Parse {
6725+
p.lexer.Unexpected()
67296726
}
6730-
p.reportPrivateNameUsage(name.String)
67316727
p.lexer.Next()
6732-
} else {
6733-
memberExpr.Data = &js_ast.EDot{
6734-
Target: memberExpr,
6735-
Name: p.lexer.Identifier.String,
6736-
NameLoc: p.lexer.Loc(),
6728+
6729+
case js_lexer.TDot:
6730+
p.lexer.Next()
6731+
6732+
if p.lexer.Token == js_lexer.TPrivateIdentifier {
6733+
name := p.lexer.Identifier
6734+
memberExpr.Data = &js_ast.EIndex{
6735+
Target: memberExpr,
6736+
Index: js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EPrivateIdentifier{Ref: p.storeNameInRef(name)}},
6737+
}
6738+
p.reportPrivateNameUsage(name.String)
6739+
p.lexer.Next()
6740+
} else {
6741+
memberExpr.Data = &js_ast.EDot{
6742+
Target: memberExpr,
6743+
Name: p.lexer.Identifier.String,
6744+
NameLoc: p.lexer.Loc(),
6745+
}
6746+
p.lexer.Expect(js_lexer.TIdentifier)
67376747
}
6738-
p.lexer.Expect(js_lexer.TIdentifier)
6739-
}
67406748

6741-
// "@x.y<z>() class{}"
6742-
if p.options.ts.Parse {
6743-
p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{})
6744-
}
6745-
}
6749+
case js_lexer.TQuestionDot:
6750+
// The grammar for "DecoratorMemberExpression" currently forbids "?."
6751+
p.lexer.Expect(js_lexer.TDot)
67466752

6747-
// The grammar for "DecoratorMemberExpression" currently forbids "?."
6748-
if p.lexer.Token == js_lexer.TQuestionDot {
6749-
p.lexer.Expect(js_lexer.TDot)
6750-
}
6753+
case js_lexer.TOpenParen:
6754+
args, closeParenLoc, isMultiLine := p.parseCallArgs()
6755+
memberExpr.Data = &js_ast.ECall{
6756+
Target: memberExpr,
6757+
Args: args,
6758+
CloseParenLoc: closeParenLoc,
6759+
IsMultiLine: isMultiLine,
6760+
Kind: js_ast.TargetWasOriginallyPropertyAccess,
6761+
}
6762+
break loop
67516763

6752-
if p.lexer.Token == js_lexer.TOpenParen {
6753-
args, closeParenLoc, isMultiLine := p.parseCallArgs()
6754-
memberExpr.Data = &js_ast.ECall{
6755-
Target: memberExpr,
6756-
Args: args,
6757-
CloseParenLoc: closeParenLoc,
6758-
IsMultiLine: isMultiLine,
6759-
Kind: js_ast.TargetWasOriginallyPropertyAccess,
6764+
default:
6765+
// "@x<y>"
6766+
// "@x.y<z>"
6767+
if !p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{}) {
6768+
break loop
6769+
}
67606770
}
67616771
}
67626772

internal/js_parser/js_parser_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,9 @@ func TestDecorators(t *testing.T) {
20642064
expectParseError(t, "@x export @y class Foo {}", "<stdin>: ERROR: Decorators are not valid here\n")
20652065
expectParseError(t, "@x export default abstract", "<stdin>: ERROR: Decorators are not valid here\n")
20662066
expectParseError(t, "@x export @y default class {}", "<stdin>: ERROR: Decorators are not valid here\n<stdin>: ERROR: Unexpected \"default\"\n")
2067+
2068+
// Disallow TypeScript syntax in JavaScript
2069+
expectParseError(t, "@x!.y!.z class Foo {}", "<stdin>: ERROR: Unexpected \"!\"\n")
20672070
}
20682071

20692072
func TestGenerator(t *testing.T) {

internal/js_parser/ts_parser_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,10 @@ func TestTSExperimentalDecorator(t *testing.T) {
20512051
expectParseErrorExperimentalDecoratorTS(t, "@x export default abstract", "<stdin>: ERROR: Decorators are not valid here\n")
20522052
expectParseErrorExperimentalDecoratorTS(t, "@x export @y default class {}", "<stdin>: ERROR: Decorators are not valid here\n<stdin>: ERROR: Unexpected \"default\"\n")
20532053

2054+
// From the TypeScript team: "We do allow postfix ! because it's TypeScript only."
2055+
// https://github.com/microsoft/TypeScript/issues/57756
2056+
expectPrintedExperimentalDecoratorTS(t, "@x!.y!.z class Foo {}", "let Foo = class {\n};\nFoo = __decorateClass([\n x.y.z\n], Foo);\n")
2057+
20542058
// TypeScript experimental decorators are actually allowed on declared and abstract fields
20552059
expectPrintedExperimentalDecoratorTS(t, "class Foo { @(() => {}) declare foo: any; @(() => {}) bar: any }",
20562060
"class Foo {\n bar;\n}\n__decorateClass([\n () => {\n }\n], Foo.prototype, \"foo\", 2);\n__decorateClass([\n () => {\n }\n], Foo.prototype, \"bar\", 2);\n")
@@ -2132,6 +2136,10 @@ func TestTSDecorators(t *testing.T) {
21322136
expectParseErrorTS(t, "@x export default abstract", "<stdin>: ERROR: Decorators are not valid here\n")
21332137
expectParseErrorTS(t, "@x export @y default class {}", "<stdin>: ERROR: Decorators are not valid here\n<stdin>: ERROR: Unexpected \"default\"\n")
21342138

2139+
// From the TypeScript team: "We do allow postfix ! because it's TypeScript only."
2140+
// https://github.com/microsoft/TypeScript/issues/57756
2141+
expectPrintedTS(t, "@x!.y!.z class Foo {}", "@x.y.z class Foo {\n}\n")
2142+
21352143
// JavaScript decorators are not allowed on declared or abstract fields
21362144
expectParseErrorTS(t, "class Foo { @(() => {}) declare foo: any; @(() => {}) bar: any }",
21372145
"<stdin>: ERROR: Decorators are not valid here\n")

0 commit comments

Comments
 (0)