diff --git a/src/quick-lint-js/fe/parse-expression.cpp b/src/quick-lint-js/fe/parse-expression.cpp
index 05c75ee9b0..619453cafc 100644
--- a/src/quick-lint-js/fe/parse-expression.cpp
+++ b/src/quick-lint-js/fe/parse-expression.cpp
@@ -3606,7 +3606,29 @@ Expression* Parser::parse_jsx_or_typescript_generic_expression(
}
break;
+ // // JSX.
+ // expr // Type assertion.
+ case Token_Type::kw_keyof:
+ case Token_Type::kw_this:
+ case Token_Type::kw_readonly:
+ case Token_Type::kw_typeof:
+ case Token_Type::kw_unique:
+ case Token_Type::number:
default:
+ // For these uncommon cases, don't try hard to guess what the user
+ // meant.
+ this->lexer_.roll_back_transaction(std::move(transaction));
+ if (this->options_.jsx) {
+ return this->parse_jsx_expression(v);
+ } else {
+ return this->parse_typescript_angle_type_assertion_expression(
+ v, prec,
+ /*is_invalid_due_to_jsx_ambiguity=*/false);
+ }
+ break;
+
+ // <>hello<> // JSX.
+ case Token_Type::greater:
break;
}
this->lexer_.roll_back_transaction(std::move(transaction));
diff --git a/test/test-parse-typescript-angle-type-assertion.cpp b/test/test-parse-typescript-angle-type-assertion.cpp
index 0e3b42ee5c..052d3b7062 100644
--- a/test/test-parse-typescript-angle-type-assertion.cpp
+++ b/test/test-parse-typescript-angle-type-assertion.cpp
@@ -117,6 +117,8 @@ TEST_F(Test_Parse_TypeScript_Angle_Type_Assertion, angle_type_assertion) {
u8"< & Type>(expr);"_sv,
u8"<[Type]>(expr);"_sv,
u8"(expr);"_sv,
+ u8"(expr);"_sv,
+ u8"(expr);"_sv,
u8"<{k: Type}>(expr);"_sv,
}) {
SCOPED_TRACE(out_string8(code));
@@ -158,6 +160,48 @@ TEST_F(Test_Parse_TypeScript_Angle_Type_Assertion, angle_type_assertion) {
}));
}
+ {
+ Spy_Visitor p = test_parse_and_visit_statement(
+ u8"expr;"_sv, no_diags, typescript_options);
+ EXPECT_THAT(p.visits, ElementsAreArray({
+ "visit_enter_type_scope", // <
+ "visit_variable_use", // v
+ "visit_exit_type_scope", // >
+ "visit_variable_use", // expr
+ }));
+ EXPECT_THAT(p.variable_uses, ElementsAreArray({u8"v"_sv, u8"expr"_sv}));
+ }
+
+ {
+ Spy_Visitor p = test_parse_and_visit_statement(
+ u8"expr;"_sv, no_diags, typescript_options);
+ EXPECT_THAT(p.visits, ElementsAreArray({
+ "visit_enter_type_scope", // <
+ "visit_exit_type_scope", // >
+ "visit_variable_use", // expr
+ }));
+ }
+
+ {
+ Spy_Visitor p = test_parse_and_visit_statement(u8"expr;"_sv, no_diags,
+ typescript_options);
+ EXPECT_THAT(p.visits, ElementsAreArray({
+ "visit_enter_type_scope", // <
+ "visit_exit_type_scope", // >
+ "visit_variable_use", // expr
+ }));
+ }
+
+ {
+ Spy_Visitor p = test_parse_and_visit_statement(u8"<42>expr;"_sv, no_diags,
+ typescript_options);
+ EXPECT_THAT(p.visits, ElementsAreArray({
+ "visit_enter_type_scope", // <
+ "visit_exit_type_scope", // >
+ "visit_variable_use", // expr
+ }));
+ }
+
for (const String8& type :
typescript_builtin_type_keywords | typescript_special_type_keywords) {
Test_Parser p(concat(u8"<"_sv, type, u8">expr;"_sv), typescript_options);