Repository navigation
Report an error when erasing as/satisfies exposes an invalid left operand of ** - #65312
Hafiar (0xABCD01) wants to merge 1 commit into
Conversation
…rand of ** An 'as' or 'satisfies' expression whose operand is a unary, await, or type assertion expression erases to a form that cannot appear as the left operand of an exponentiation expression, e.g. '-x as number ** 2' erases to '-x ** 2', which is a syntax error in JavaScript. The parser already reports TS17006/TS17007 for the unasserted forms; report the same errors when the assertion is present. Fixes microsoft#65170
|
Hafiar (@0xABCD01) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are reported for the change to exponentiation operand checks.
No open issues 🎉
What changed in this PR
Addresses #65170 by extending the native TypeScript parser’s exponentiation checks to reject as/satisfies expressions whose erasure exposes an invalid left operand of **.
Changes:
- Unwraps assertion chains and reuses TS17006/TS17007 diagnostics for invalid operands.
- Adds 23 regression cases covering unsafe forms and valid parenthesized, update-expression, and right-operand forms.
- Updates diagnostic, JavaScript emit, type, and symbol baselines.
| File | Description |
|---|---|
tsc/testdata/tests/cases/compiler/disallowUnerasableAssertion.ts |
Adds 23 regression cases for assertion erasure around exponentiation. |
tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.types |
Records types for the new cases. |
tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.symbols |
Records symbols for the new cases. |
tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.js |
Records emitted JavaScript for the new cases. |
tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.errors.txt |
Adds 14 expected diagnostics while preserving existing errors. |
tsc/internal/parser/parser.go |
Checks assertion-wrapped left operands when parsing **. |
Review effort: Balanced
Fixes #65170.
Problem
When an
asorsatisfiesexpression wraps a unary expression,await, or a type assertion and the result is used as the left operand of**, no diagnostic is reported — even though erasing the assertion produces JavaScript that is a syntax error:Both compile silently today, but the erased output is
-x ** 2/await x ** 2, which Node rejects. The parser already rejects the unasserted forms with TS17006/TS17007, and #63527/#63661 reject assertion forms whose erasure changes the grouping of a binary expression; this case is the remaining gap.Explicitly parenthesized forms such as
(-x) as number ** 2and(await x) as number ** 2remain accepted, as in the issue.Implementation
There is a single site where
**expressions are formed, inparseBinaryExpressionRest. A new helper,checkExponentiationLeftOperand, runs there: it unwraps chainedas/satisfiesexpressions (including mixed chains) from the left operand, and if the unwrapped core is a unary expression (+,-,~,!,typeof,void,delete), anawaitexpression, or a type assertion, it reports the existing TS17006 (TS17007 for a type assertion) with the correct operator name — the same diagnostics the parser already produces for the unasserted forms, over the whole left operand. No new diagnostic messages, and no generated files change.Update expressions are deliberately not reported:
++y as number ** 2erases to++y ** 2, which is valid. Operands whose core is a call, member access, template, object/function expression,new, orthisare likewise untouched, as are fully parenthesized operands and the right operand of**.Two design choices worth flagging:
erasableSyntaxOnlyshould error on unerasableas/satisfies#63527/as/satisfiesbetween exponentiation operators cannot be safely erased #63661 rejections. If you would prefer them gated on--erasableSyntaxOnly— the fallback the reporter said would also work — that is a one-condition change incheckExponentiationLeftOperand.erasableSyntaxOnlyshould error on unerasableas/satisfies#63527/as/satisfiesbetween exponentiation operators cannot be safely erased #63661, because a precise diagnostic already exists for this violation; I can switch to the break mechanism if you prefer consistency there.Tests
23 new cases (z01–z23) in
disallowUnerasableAssertion.ts, covering all six unary operator classes plusawaitand the type-assertion form,satisfies, a mixedas→satisfieschain, parentheses inside a unary expression, a non-null core, and the main safe shapes (parenthesized operands, plain operand,*instead of**, prefix++, assertion on the right operand).The accepted baseline for that file now reports 26 errors: the 12 pre-existing TS1005 errors are unchanged, plus 14 new ones (13× TS17006, 1× TS17007) at the new cases; the baseline diff is otherwise purely additive. The new cases were verified red on unmodified main (all silent there) and green with this change.
go testpasses for./tsc/internal/parser/and for thedisallowUnerasableAssertiontestrunner suite, and the full baseline testrunner corpus passes.npx hereby tidyis clean.Disclosure
This change was authored with AI assistance. I have read and understand the patch, and I will respond to review feedback myself.