Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support of TS 4.7 Instantiation Expression #2038

Merged
merged 1 commit into from Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/js_parser/js_parser.go
Expand Up @@ -3214,6 +3214,9 @@ func (p *parser) parsePrefix(level js_ast.L, errors *deferredErrors, flags exprF
})}
}

// Allow `const a = b<c>`
p.trySkipTypeScriptTypeArgumentsWithBacktracking()

ref := p.storeNameInRef(name)
return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}}

Expand Down
17 changes: 6 additions & 11 deletions internal/js_parser/ts_parser.go
Expand Up @@ -364,6 +364,7 @@ func (p *parser) skipTypeScriptTypeWithOpts(level js_ast.L, opts skipTypeOpts) {
}
p.lexer.Next()
}
p.skipTypeScriptTypeArguments(false /* isInsideJSXElement */)
}

case js_lexer.TOpenBracket:
Expand Down Expand Up @@ -785,10 +786,12 @@ func (p *parser) canFollowTypeArgumentsInExpression() bool {
return true

case
// These cases can't legally follow a type arg list. However, they're not
// legal expressions either. The user is probably in the middle of a
// generic type. So treat it as such.
// These tokens can't follow in a call expression,
// nor can they start an expression.
// So, consider the type argument list part of an instantiation expression.
js_lexer.TComma, // foo<x>,
js_lexer.TDot, // foo<x>.
js_lexer.TQuestionDot, // foo<x>?.
js_lexer.TCloseParen, // foo<x>)
js_lexer.TCloseBracket, // foo<x>]
js_lexer.TColon, // foo<x>:
Expand All @@ -808,14 +811,6 @@ func (p *parser) canFollowTypeArgumentsInExpression() bool {
js_lexer.TEndOfFile: // foo<x>
return true

case
// We don't want to treat these as type arguments. Otherwise we'll parse
// this as an invocation expression. Instead, we want to parse out the
// expression in isolation from the type arguments.
js_lexer.TComma, // foo<x>,
js_lexer.TOpenBrace: // foo<x> {
return false

default:
// Anything else treat as an expression.
return false
Expand Down
45 changes: 45 additions & 0 deletions internal/js_parser/ts_parser_test.go
Expand Up @@ -1549,6 +1549,8 @@ func TestTSNew(t *testing.T) {
expectPrintedTS(t, "new Foo()", "new Foo();\n")
expectPrintedTS(t, "new Foo<number>()", "new Foo();\n")
expectPrintedTS(t, "new Foo<number, boolean>()", "new Foo();\n")
expectPrintedTS(t, "new Foo<number>", "new Foo();\n")
expectPrintedTS(t, "new Foo<number, boolean>", "new Foo();\n")

expectPrintedTS(t, "new Foo!()", "new Foo();\n")
expectPrintedTS(t, "new Foo!<number>()", "new Foo();\n")
Expand All @@ -1562,6 +1564,49 @@ func TestTSNew(t *testing.T) {
expectParseError(t, "new Foo!()", "<stdin>: ERROR: Unexpected \"!\"\n")
}

func TestTSInstantiationExpression(t *testing.T) {
expectPrintedTS(t, "f<number>", "f;\n")
expectPrintedTS(t, "f<number, boolean>", "f;\n")
expectPrintedTS(t, "f.g<number>", "f.g;\n")
expectPrintedTS(t, "f<number>.g", "f.g;\n")
expectPrintedTS(t, "f<number>.g<number>", "f.g;\n")
expectPrintedTS(t, "f['g']<number>", "f[\"g\"];\n")
expectPrintedTS(t, "(f<number>)<number>", "f;\n")

// function call
expectPrintedTS(t, "const x1 = f<true>\n(true);", "const x1 = f(true);\n")
// relational expression
expectPrintedTS(t, "const x1 = f<true>\ntrue;", "const x1 = f < true > true;\n")
// instantiation expression
expectPrintedTS(t, "const x1 = f<true>;\n(true);", "const x1 = f;\ntrue;\n")

expectPrintedTS(t, "f<number>?.();", "f?.();\n")
expectPrintedTS(t, "f?.<number>();", "f?.();\n")

expectPrintedTS(t, "f<number>['g'];", "f < number > [\"g\"];\n")

expectPrintedTS(t, "type T21 = typeof Array<string>; f();", "f();\n")
expectPrintedTS(t, "type T22 = typeof Array<string, number>; f();", "f();\n")

expectPrintedTS(t, "f<x>, g<y>;", "f, g;\n")
expectPrintedTS(t, "[f<x>];", "[f];\n")
expectPrintedTS(t, "f<x> ? g<y> : h<z>;", "f ? g : h;\n")
expectPrintedTS(t, "f<x> ^ g<y>;", "f ^ g;\n")
expectPrintedTS(t, "f<x> & g<y>;", "f & g;\n")
expectPrintedTS(t, "f<x> | g<y>;", "f | g;\n")
expectPrintedTS(t, "f<x> && g<y>;", "f && g;\n")
expectPrintedTS(t, "f<x> || g<y>;", "f || g;\n")
expectPrintedTS(t, "f<x> ?? g<y>;", "f ?? g;\n")
expectPrintedTS(t, "{ f<x> }", "{\n f;\n}\n")
expectPrintedTS(t, "f<x> == g<y>;", "f == g;\n")
expectPrintedTS(t, "f<x> === g<y>;", "f === g;\n")
expectPrintedTS(t, "f<x> != g<y>;", "f != g;\n")
expectPrintedTS(t, "f<x> !== g<y>;", "f !== g;\n")

expectParseErrorTS(t, "const a8 = f<number><number>;", "<stdin>: ERROR: Unexpected \";\"\n")
expectParseErrorTS(t, "const b1 = f?.<number>;", "<stdin>: ERROR: Expected \"(\" but found \";\"\n")
}

func TestTSExponentiation(t *testing.T) {
// More info: https://github.com/microsoft/TypeScript/issues/41755
expectParseErrorTS(t, "await x! ** 2", "<stdin>: ERROR: Unexpected \"**\"\n")
Expand Down