Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 52 additions & 17 deletions crates/swc_ecma_lexer/src/common/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,26 @@ pub fn parse_bin_op_recursively<'a>(
}
}

/// Returns true if the type cannot be followed by type parameters.
/// After these types, `<` should be lexed as a comparison operator, not as
/// the start of type parameters.
fn type_cannot_have_type_params(type_ann: &TsType) -> bool {
matches!(
type_ann,
TsType::TsKeywordType(_)
| TsType::TsLitType(_)
| TsType::TsThisType(_)
| TsType::TsArrayType(_)
| TsType::TsTupleType(_)
| TsType::TsUnionOrIntersectionType(_)
| TsType::TsTypePredicate(_)
| TsType::TsIndexedAccessType(_)
| TsType::TsConditionalType(_)
| TsType::TsMappedType(_)
| TsType::TsTypeOperator(_)
)
}

/// Returns `(left, Some(next_prec))` or `(expr, None)`.
fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>(
p: &mut P,
Expand All @@ -1308,39 +1328,54 @@ fn parse_bin_op_recursively_inner<'a, P: Parser<'a>>(
if PREC_OF_IN > min_prec && p.input().is(&P::Token::AS) {
let start = left.span_lo();
let expr = left;
let node = if peek!(p).is_some_and(|cur| cur.is_const()) {
return if peek!(p).is_some_and(|cur| cur.is_const()) {
p.bump(); // as
p.bump(); // const
TsConstAssertion {
let node = TsConstAssertion {
span: p.span(start),
expr,
}
.into()
.into();
// as const is treated as primitive type
p.do_inside_of_context(Context::ShouldNotLexLtOrGtAsType, |p| {
parse_bin_op_recursively_inner(p, node, min_prec)
})
} else {
let type_ann = next_then_parse_ts_type(p)?;
TsAsExpr {
let prevent_type_params = type_cannot_have_type_params(&type_ann);
let node = TsAsExpr {
span: p.span(start),
expr,
type_ann,
}
.into()
.into();
if prevent_type_params {
p.do_inside_of_context(Context::ShouldNotLexLtOrGtAsType, |p| {
parse_bin_op_recursively_inner(p, node, min_prec)
})
} else {
parse_bin_op_recursively_inner(p, node, min_prec)
}
};

return parse_bin_op_recursively_inner(p, node, min_prec);
} else if p.input().is(&P::Token::SATISFIES) {
let start = left.span_lo();
let expr = left;
let node = {
let type_ann = next_then_parse_ts_type(p)?;
TsSatisfiesExpr {
span: p.span(start),
expr,
type_ann,
}
.into()
};
let type_ann = next_then_parse_ts_type(p)?;
let prevent_type_params = type_cannot_have_type_params(&type_ann);
let node = TsSatisfiesExpr {
span: p.span(start),
expr,
type_ann,
}
.into();

return parse_bin_op_recursively_inner(p, node, min_prec);
return if prevent_type_params {
p.do_inside_of_context(Context::ShouldNotLexLtOrGtAsType, |p| {
parse_bin_op_recursively_inner(p, node, min_prec)
})
} else {
parse_bin_op_recursively_inner(p, node, min_prec)
};
}
}

Expand Down
12 changes: 6 additions & 6 deletions crates/swc_ecma_lexer/src/common/parser/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -931,12 +931,12 @@ pub(super) fn next_then_parse_ts_type<'a, P: Parser<'a>>(p: &mut P) -> PResult<B
parse_ts_type(p)
});

if !p.ctx().contains(Context::InType) && {
let cur = p.input().cur();
cur.is_less() || cur.is_greater()
} {
p.input_mut().merge_lt_gt();
}
// if !p.ctx().contains(Context::InType) && {
// let cur = p.input().cur();
// cur.is_less() || cur.is_greater()
// } {
// p.input_mut().merge_lt_gt();
// }

result
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Primitive keyword types - >= should be comparison operator
i as number >= 5;
i as string >= "hello";
i as boolean >= true;
i as any >= foo;
i as void >= bar;
i as undefined >= baz;
i as null >= qux;
i as never >= abc;
i as unknown >= def;
i as object >= ghi;
i as symbol >= jkl;
i as bigint >= 10n;

// satisfies with primitive types
i satisfies number >= 5;
i satisfies string >= "test";

// Literal types - should be treated as primitives
i as 2 >= 5;
i as "x" >= "y";
i as true >= false;
i as 10n >= 20n;

// this type - non-callable
i as this >= 5;

// Array types
i as X[] >= 5;

// Generic types
i as X<Y> >= 5;

// Tuple types - non-callable
i as [X, Y] >= 5;

// Union types - non-callable
i as A | string >= 5;
i as A | B<T> >= 5;

// Intersection types - non-callable
i as A & string >= 5;
i as A & B<T> >= 5;

// Type operators - non-callable
i as keyof T<U> >= 5;
i as readonly number[] >= 5;
i as unique symbol >= 5;

// Indexed access types - non-callable
i as T[K] >= 5;

// Conditional types - non-callable
i as T extends U ? X : string >= 5;
i as T extends U ? X : Y<T> >= 5;

// Mapped types - non-callable
i as { [K in keyof T]: V } >= 5;
Loading