Skip to content

Commit

Permalink
add support of TS 4.7 Instantiation Expression (evanw#2038)
Browse files Browse the repository at this point in the history
  • Loading branch information
g-plane authored and zhusjfaker committed Mar 28, 2022
1 parent 58de494 commit 45bc585
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 11 deletions.
3 changes: 3 additions & 0 deletions internal/js_parser/js_parser.go
Expand Up @@ -2977,6 +2977,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 @@ -376,6 +376,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 @@ -797,10 +798,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 @@ -820,14 +823,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 @@ -1564,6 +1564,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 @@ -1577,6 +1579,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

0 comments on commit 45bc585

Please sign in to comment.