From 504d56868593224a900a24aa62267c84ab35ca38 Mon Sep 17 00:00:00 2001 From: 0xABCD01 <0xABCD01@users.noreply.github.com> Date: Wed, 4 Nov 2026 06:56:43 +0000 Subject: [PATCH] Report an error when erasing as/satisfies exposes an invalid left operand 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 #65170 --- tsc/internal/parser/parser.go | 40 ++++ .../disallowUnerasableAssertion.errors.txt | 80 ++++++- .../compiler/disallowUnerasableAssertion.js | 59 +++++ .../disallowUnerasableAssertion.symbols | 114 ++++++++++ .../disallowUnerasableAssertion.types | 215 ++++++++++++++++++ .../compiler/disallowUnerasableAssertion.ts | 36 +++ 6 files changed, 543 insertions(+), 1 deletion(-) diff --git a/tsc/internal/parser/parser.go b/tsc/internal/parser/parser.go index b1c20ccf6109c..3ecf74ff10455 100644 --- a/tsc/internal/parser/parser.go +++ b/tsc/internal/parser/parser.go @@ -4784,6 +4784,9 @@ func (p *Parser) parseBinaryExpressionRest(precedence ast.OperatorPrecedence, le } } } else { + if operator == ast.KindAsteriskAsteriskToken { + p.checkExponentiationLeftOperand(leftOperand) + } leftOperand = p.makeBinaryExpression(leftOperand, p.parseTokenNode(), p.parseBinaryExpressionOrHigher(newPrecedence), pos) lastOperand = leftOperand } @@ -4791,6 +4794,43 @@ func (p *Parser) parseBinaryExpressionRest(precedence ast.OperatorPrecedence, le return leftOperand } +// checkExponentiationLeftOperand reports an error when the left operand of an +// exponentiation expression is an 'as' or 'satisfies' expression whose operand could +// not appear in that position once the assertion is erased, mirroring the errors +// reported for the unasserted forms in parseUnaryExpressionOrHigher. For example, +// '-x as number ** 2' erases to '-x ** 2', which is not valid JavaScript. +// See https://github.com/microsoft/TypeScript/issues/65170. +func (p *Parser) checkExponentiationLeftOperand(leftOperand *ast.Expression) { + expression := leftOperand + for expression.Kind == ast.KindAsExpression || expression.Kind == ast.KindSatisfiesExpression { + expression = expression.Expression() + } + if expression == leftOperand { + return + } + pos := scanner.SkipTrivia(p.sourceText, leftOperand.Pos()) + end := leftOperand.End() + switch expression.Kind { + case ast.KindPrefixUnaryExpression: + // Prefix '++' and '--' form update expressions, which are valid left + // operands of an exponentiation expression even after erasure. + switch operator := expression.AsPrefixUnaryExpression().Operator; operator { + case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken, ast.KindExclamationToken: + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(operator)) + } + case ast.KindDeleteExpression: + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindDeleteKeyword)) + case ast.KindTypeOfExpression: + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindTypeOfKeyword)) + case ast.KindVoidExpression: + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindVoidKeyword)) + case ast.KindAwaitExpression: + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, scanner.TokenToString(ast.KindAwaitKeyword)) + case ast.KindTypeAssertionExpression: + p.parseErrorAt(pos, end, diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses) + } +} + func (p *Parser) makeSatisfiesExpression(expression *ast.Expression, typeNode *ast.TypeNode) *ast.Node { return p.checkJSSyntax(p.finishNode(p.factory.NewSatisfiesExpression(expression, typeNode), expression.Pos())) } diff --git a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.errors.txt b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.errors.txt index 17e08558907c4..d5dcb8ce112bb 100644 --- a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.errors.txt @@ -10,9 +10,23 @@ disallowUnerasableAssertion.ts(71,44): error TS1005: ',' expected. disallowUnerasableAssertion.ts(72,58): error TS1005: ',' expected. disallowUnerasableAssertion.ts(79,44): error TS1005: ',' expected. disallowUnerasableAssertion.ts(80,58): error TS1005: ',' expected. +disallowUnerasableAssertion.ts(93,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(94,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(95,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(96,20): error TS17006: An unary expression with the '+' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(97,20): error TS17006: An unary expression with the '~' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(98,20): error TS17006: An unary expression with the '!' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(99,20): error TS17006: An unary expression with the 'typeof' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(100,20): error TS17006: An unary expression with the 'void' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(101,20): error TS17006: An unary expression with the 'delete' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(102,38): error TS17006: An unary expression with the 'await' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(103,20): error TS17007: A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(104,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(105,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. +disallowUnerasableAssertion.ts(106,20): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. -==== disallowUnerasableAssertion.ts (12 errors) ==== +==== disallowUnerasableAssertion.ts (26 errors) ==== // https://github.com/microsoft/TypeScript/issues/63527 // Expressions of the form 'a ## b as T $$ c' are errors when $$ would bind before ## after @@ -117,4 +131,68 @@ disallowUnerasableAssertion.ts(80,58): error TS1005: ',' expected. export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error ~~ !!! error TS1005: ',' expected. + + // https://github.com/microsoft/TypeScript/issues/65170 + + // Erasing an assertion must not expose an invalid left operand of '**' either. + // A unary, await, or type assertion expression in that position is an error, + // just as it is without the assertion. + + declare const num: number; + declare const promised: Promise; + declare const obj: { y?: number }; + declare let mutableNum: number; + + export const z01 = -num as number ** 2; // Error + ~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z02 = -num satisfies number ** 2; // Error + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z03 = -num as any as number ** 2; // Error + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z04 = +num as number ** 2; // Error + ~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '+' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z05 = ~num as number ** 2; // Error + ~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '~' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z06 = !num as any ** 2; // Error + ~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '!' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z07 = typeof num as any ** 2; // Error + ~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the 'typeof' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z08 = void num as any ** 2; // Error + ~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the 'void' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z09 = delete obj.y as any ** 2; // Error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the 'delete' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export async function z10() { return await promised as number ** 2; } // Error + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the 'await' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z11 = -num as any ** 2; // Error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS17007: A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z12 = -(num) as number ** 2; // Error + ~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z13 = -num! as number ** 2; // Error + ~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + export const z14 = -num as number satisfies number ** 2; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses. + + export const z15 = (-num) as number ** 2; + export async function z16() { return (await promised) as number ** 2; } + export const z17 = num as number ** 2; + export const z18 = -num as number * 2; + export const z19 = num! as number ** 2; + export const z20 = (-num)! as number ** 2; + export const z21 = (-num as number) as any ** 2; + export const z22 = ++mutableNum as number ** 2; + export const z23 = 2 ** -num as number; \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.js b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.js index 74e2fc8514c38..5f4b4b7057008 100644 --- a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.js +++ b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.js @@ -81,6 +81,42 @@ export const y26 = 2 * 3 satisfies any satisfies number * 2; // Equal-precedence right-associative operators do not. export const y27 = 2 ** 3 satisfies number ** 2; // Error export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error + +// https://github.com/microsoft/TypeScript/issues/65170 + +// Erasing an assertion must not expose an invalid left operand of '**' either. +// A unary, await, or type assertion expression in that position is an error, +// just as it is without the assertion. + +declare const num: number; +declare const promised: Promise; +declare const obj: { y?: number }; +declare let mutableNum: number; + +export const z01 = -num as number ** 2; // Error +export const z02 = -num satisfies number ** 2; // Error +export const z03 = -num as any as number ** 2; // Error +export const z04 = +num as number ** 2; // Error +export const z05 = ~num as number ** 2; // Error +export const z06 = !num as any ** 2; // Error +export const z07 = typeof num as any ** 2; // Error +export const z08 = void num as any ** 2; // Error +export const z09 = delete obj.y as any ** 2; // Error +export async function z10() { return await promised as number ** 2; } // Error +export const z11 = -num as any ** 2; // Error +export const z12 = -(num) as number ** 2; // Error +export const z13 = -num! as number ** 2; // Error +export const z14 = -num as number satisfies number ** 2; // Error + +export const z15 = (-num) as number ** 2; +export async function z16() { return (await promised) as number ** 2; } +export const z17 = num as number ** 2; +export const z18 = -num as number * 2; +export const z19 = num! as number ** 2; +export const z20 = (-num)! as number ** 2; +export const z21 = (-num as number) as any ** 2; +export const z22 = ++mutableNum as number ** 2; +export const z23 = 2 ** -num as number; //// [disallowUnerasableAssertion.js] @@ -159,3 +195,26 @@ export const y27 = 2 ** 3; ** 2; // Error export const y28 = 2 ** 3; ** 2; // Error +export const z01 = (-num) ** 2; // Error +export const z02 = (-num) ** 2; // Error +export const z03 = (-num) ** 2; // Error +export const z04 = (+num) ** 2; // Error +export const z05 = (~num) ** 2; // Error +export const z06 = (!num) ** 2; // Error +export const z07 = (typeof num) ** 2; // Error +export const z08 = (void num) ** 2; // Error +export const z09 = (delete obj.y) ** 2; // Error +export async function z10() { return (await promised) ** 2; } // Error +export const z11 = (-num) ** 2; // Error +export const z12 = (-(num)) ** 2; // Error +export const z13 = (-num) ** 2; // Error +export const z14 = (-num) ** 2; // Error +export const z15 = (-num) ** 2; +export async function z16() { return (await promised) ** 2; } +export const z17 = num ** 2; +export const z18 = -num * 2; +export const z19 = num ** 2; +export const z20 = (-num) ** 2; +export const z21 = (-num) ** 2; +export const z22 = (++mutableNum) ** 2; +export const z23 = 2 ** -num; diff --git a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.symbols b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.symbols index 0ac35f6b4e174..390215d6bfa5e 100644 --- a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.symbols +++ b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.symbols @@ -178,3 +178,117 @@ export const y27 = 2 ** 3 satisfies number ** 2; // Error export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error >y28 : Symbol(y28, Decl(disallowUnerasableAssertion.ts, 79, 12)) +// https://github.com/microsoft/TypeScript/issues/65170 + +// Erasing an assertion must not expose an invalid left operand of '**' either. +// A unary, await, or type assertion expression in that position is an error, +// just as it is without the assertion. + +declare const num: number; +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +declare const promised: Promise; +>promised : Symbol(promised, Decl(disallowUnerasableAssertion.ts, 88, 13)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) + +declare const obj: { y?: number }; +>obj : Symbol(obj, Decl(disallowUnerasableAssertion.ts, 89, 13)) +>y : Symbol(y, Decl(disallowUnerasableAssertion.ts, 89, 20)) + +declare let mutableNum: number; +>mutableNum : Symbol(mutableNum, Decl(disallowUnerasableAssertion.ts, 90, 11)) + +export const z01 = -num as number ** 2; // Error +>z01 : Symbol(z01, Decl(disallowUnerasableAssertion.ts, 92, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z02 = -num satisfies number ** 2; // Error +>z02 : Symbol(z02, Decl(disallowUnerasableAssertion.ts, 93, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z03 = -num as any as number ** 2; // Error +>z03 : Symbol(z03, Decl(disallowUnerasableAssertion.ts, 94, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z04 = +num as number ** 2; // Error +>z04 : Symbol(z04, Decl(disallowUnerasableAssertion.ts, 95, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z05 = ~num as number ** 2; // Error +>z05 : Symbol(z05, Decl(disallowUnerasableAssertion.ts, 96, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z06 = !num as any ** 2; // Error +>z06 : Symbol(z06, Decl(disallowUnerasableAssertion.ts, 97, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z07 = typeof num as any ** 2; // Error +>z07 : Symbol(z07, Decl(disallowUnerasableAssertion.ts, 98, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z08 = void num as any ** 2; // Error +>z08 : Symbol(z08, Decl(disallowUnerasableAssertion.ts, 99, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z09 = delete obj.y as any ** 2; // Error +>z09 : Symbol(z09, Decl(disallowUnerasableAssertion.ts, 100, 12)) +>obj.y : Symbol(y, Decl(disallowUnerasableAssertion.ts, 89, 20)) +>obj : Symbol(obj, Decl(disallowUnerasableAssertion.ts, 89, 13)) +>y : Symbol(y, Decl(disallowUnerasableAssertion.ts, 89, 20)) + +export async function z10() { return await promised as number ** 2; } // Error +>z10 : Symbol(z10, Decl(disallowUnerasableAssertion.ts, 100, 44)) +>promised : Symbol(promised, Decl(disallowUnerasableAssertion.ts, 88, 13)) + +export const z11 = -num as any ** 2; // Error +>z11 : Symbol(z11, Decl(disallowUnerasableAssertion.ts, 102, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z12 = -(num) as number ** 2; // Error +>z12 : Symbol(z12, Decl(disallowUnerasableAssertion.ts, 103, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z13 = -num! as number ** 2; // Error +>z13 : Symbol(z13, Decl(disallowUnerasableAssertion.ts, 104, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z14 = -num as number satisfies number ** 2; // Error +>z14 : Symbol(z14, Decl(disallowUnerasableAssertion.ts, 105, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z15 = (-num) as number ** 2; +>z15 : Symbol(z15, Decl(disallowUnerasableAssertion.ts, 107, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export async function z16() { return (await promised) as number ** 2; } +>z16 : Symbol(z16, Decl(disallowUnerasableAssertion.ts, 107, 41)) +>promised : Symbol(promised, Decl(disallowUnerasableAssertion.ts, 88, 13)) + +export const z17 = num as number ** 2; +>z17 : Symbol(z17, Decl(disallowUnerasableAssertion.ts, 109, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z18 = -num as number * 2; +>z18 : Symbol(z18, Decl(disallowUnerasableAssertion.ts, 110, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z19 = num! as number ** 2; +>z19 : Symbol(z19, Decl(disallowUnerasableAssertion.ts, 111, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z20 = (-num)! as number ** 2; +>z20 : Symbol(z20, Decl(disallowUnerasableAssertion.ts, 112, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z21 = (-num as number) as any ** 2; +>z21 : Symbol(z21, Decl(disallowUnerasableAssertion.ts, 113, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + +export const z22 = ++mutableNum as number ** 2; +>z22 : Symbol(z22, Decl(disallowUnerasableAssertion.ts, 114, 12)) +>mutableNum : Symbol(mutableNum, Decl(disallowUnerasableAssertion.ts, 90, 11)) + +export const z23 = 2 ** -num as number; +>z23 : Symbol(z23, Decl(disallowUnerasableAssertion.ts, 115, 12)) +>num : Symbol(num, Decl(disallowUnerasableAssertion.ts, 87, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.types b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.types index 81ee23f54210a..cbc877517d882 100644 --- a/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.types +++ b/tsc/testdata/baselines/reference/compiler/disallowUnerasableAssertion.types @@ -550,3 +550,218 @@ export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error > : any >2 : 2 +// https://github.com/microsoft/TypeScript/issues/65170 + +// Erasing an assertion must not expose an invalid left operand of '**' either. +// A unary, await, or type assertion expression in that position is an error, +// just as it is without the assertion. + +declare const num: number; +>num : number + +declare const promised: Promise; +>promised : Promise + +declare const obj: { y?: number }; +>obj : { y?: number; } +>y : number | undefined + +declare let mutableNum: number; +>mutableNum : number + +export const z01 = -num as number ** 2; // Error +>z01 : number +>-num as number ** 2 : number +>-num as number : number +>-num : number +>num : number +>2 : 2 + +export const z02 = -num satisfies number ** 2; // Error +>z02 : number +>-num satisfies number ** 2 : number +>-num satisfies number : number +>-num : number +>num : number +>2 : 2 + +export const z03 = -num as any as number ** 2; // Error +>z03 : number +>-num as any as number ** 2 : number +>-num as any as number : number +>-num as any : any +>-num : number +>num : number +>2 : 2 + +export const z04 = +num as number ** 2; // Error +>z04 : number +>+num as number ** 2 : number +>+num as number : number +>+num : number +>num : number +>2 : 2 + +export const z05 = ~num as number ** 2; // Error +>z05 : number +>~num as number ** 2 : number +>~num as number : number +>~num : number +>num : number +>2 : 2 + +export const z06 = !num as any ** 2; // Error +>z06 : number +>!num as any ** 2 : number +>!num as any : any +>!num : boolean +>num : number +>2 : 2 + +export const z07 = typeof num as any ** 2; // Error +>z07 : number +>typeof num as any ** 2 : number +>typeof num as any : any +>typeof num : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>num : number +>2 : 2 + +export const z08 = void num as any ** 2; // Error +>z08 : number +>void num as any ** 2 : number +>void num as any : any +>void num : undefined +>num : number +>2 : 2 + +export const z09 = delete obj.y as any ** 2; // Error +>z09 : number +>delete obj.y as any ** 2 : number +>delete obj.y as any : any +>delete obj.y : boolean +>obj.y : number | undefined +>obj : { y?: number; } +>y : number | undefined +>2 : 2 + +export async function z10() { return await promised as number ** 2; } // Error +>z10 : () => Promise +>await promised as number ** 2 : number +>await promised as number : number +>await promised : number +>promised : Promise +>2 : 2 + +export const z11 = -num as any ** 2; // Error +>z11 : number +>-num as any ** 2 : number +>-num as any : any +>-num : number +>-num : number +>num : number +>2 : 2 + +export const z12 = -(num) as number ** 2; // Error +>z12 : number +>-(num) as number ** 2 : number +>-(num) as number : number +>-(num) : number +>(num) : number +>num : number +>2 : 2 + +export const z13 = -num! as number ** 2; // Error +>z13 : number +>-num! as number ** 2 : number +>-num! as number : number +>-num! : number +>num! : number +>num : number +>2 : 2 + +export const z14 = -num as number satisfies number ** 2; // Error +>z14 : number +>-num as number satisfies number ** 2 : number +>-num as number satisfies number : number +>-num as number : number +>-num : number +>num : number +>2 : 2 + +export const z15 = (-num) as number ** 2; +>z15 : number +>(-num) as number ** 2 : number +>(-num) as number : number +>(-num) : number +>-num : number +>num : number +>2 : 2 + +export async function z16() { return (await promised) as number ** 2; } +>z16 : () => Promise +>(await promised) as number ** 2 : number +>(await promised) as number : number +>(await promised) : number +>await promised : number +>promised : Promise +>2 : 2 + +export const z17 = num as number ** 2; +>z17 : number +>num as number ** 2 : number +>num as number : number +>num : number +>2 : 2 + +export const z18 = -num as number * 2; +>z18 : number +>-num as number * 2 : number +>-num as number : number +>-num : number +>num : number +>2 : 2 + +export const z19 = num! as number ** 2; +>z19 : number +>num! as number ** 2 : number +>num! as number : number +>num! : number +>num : number +>2 : 2 + +export const z20 = (-num)! as number ** 2; +>z20 : number +>(-num)! as number ** 2 : number +>(-num)! as number : number +>(-num)! : number +>(-num) : number +>-num : number +>num : number +>2 : 2 + +export const z21 = (-num as number) as any ** 2; +>z21 : number +>(-num as number) as any ** 2 : number +>(-num as number) as any : any +>(-num as number) : number +>-num as number : number +>-num : number +>num : number +>2 : 2 + +export const z22 = ++mutableNum as number ** 2; +>z22 : number +>++mutableNum as number ** 2 : number +>++mutableNum as number : number +>++mutableNum : number +>mutableNum : number +>2 : 2 + +export const z23 = 2 ** -num as number; +>z23 : number +>2 ** -num as number : number +>2 ** -num : number +>2 : 2 +>-num : number +>num : number + diff --git a/tsc/testdata/tests/cases/compiler/disallowUnerasableAssertion.ts b/tsc/testdata/tests/cases/compiler/disallowUnerasableAssertion.ts index dd13824e6742e..8461a36bbd60b 100644 --- a/tsc/testdata/tests/cases/compiler/disallowUnerasableAssertion.ts +++ b/tsc/testdata/tests/cases/compiler/disallowUnerasableAssertion.ts @@ -78,3 +78,39 @@ export const y26 = 2 * 3 satisfies any satisfies number * 2; // Equal-precedence right-associative operators do not. export const y27 = 2 ** 3 satisfies number ** 2; // Error export const y28 = 2 ** 3 satisfies any satisfies number ** 2; // Error + +// https://github.com/microsoft/TypeScript/issues/65170 + +// Erasing an assertion must not expose an invalid left operand of '**' either. +// A unary, await, or type assertion expression in that position is an error, +// just as it is without the assertion. + +declare const num: number; +declare const promised: Promise; +declare const obj: { y?: number }; +declare let mutableNum: number; + +export const z01 = -num as number ** 2; // Error +export const z02 = -num satisfies number ** 2; // Error +export const z03 = -num as any as number ** 2; // Error +export const z04 = +num as number ** 2; // Error +export const z05 = ~num as number ** 2; // Error +export const z06 = !num as any ** 2; // Error +export const z07 = typeof num as any ** 2; // Error +export const z08 = void num as any ** 2; // Error +export const z09 = delete obj.y as any ** 2; // Error +export async function z10() { return await promised as number ** 2; } // Error +export const z11 = -num as any ** 2; // Error +export const z12 = -(num) as number ** 2; // Error +export const z13 = -num! as number ** 2; // Error +export const z14 = -num as number satisfies number ** 2; // Error + +export const z15 = (-num) as number ** 2; +export async function z16() { return (await promised) as number ** 2; } +export const z17 = num as number ** 2; +export const z18 = -num as number * 2; +export const z19 = num! as number ** 2; +export const z20 = (-num)! as number ** 2; +export const z21 = (-num as number) as any ** 2; +export const z22 = ++mutableNum as number ** 2; +export const z23 = 2 ** -num as number;