Skip to content

Commit a28672f

Browse files
JeanMechecrisbeto
authored andcommitted
fix(compiler): Keep paraenthesis in Nullish + Boolean expression. (#63292)
Ts 5.9 introduced a regression coming from 5.8 when parenthesis aren't generated for expressions like (`(a ?? b) && c`). This fix works around this explicitly specifying that we want to keep those parenthesis that we're aware of in this specific case; This change can be reverted if the root issue (microsoft/TypeScript#61369) is fixed. (but let's keep the tests in any case for the coverage) fixes #63287 PR Close #63292
1 parent 65c5a73 commit a28672f

File tree

4 files changed

+40
-0
lines changed

4 files changed

+40
-0
lines changed

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/GOLDEN_PARTIAL.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-
182182
<div>{{ (x && y) ?? z }}</div>
183183
<div>{{ x && (y ?? z) }}</div>
184184
<div>{{ x?.y ?? y?.z }}</div>
185+
<div>{{ (x?.y ?? y) || z }}</div>
186+
<div>{{ (x?.y ?? y) && z }}</div>
187+
<div>{{ z || (x?.y ?? y) }}</div>
188+
<div>{{ z && (x?.y ?? y) }}</div>
185189
`, isInline: true });
186190
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
187191
type: Component,
@@ -191,6 +195,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
191195
<div>{{ (x && y) ?? z }}</div>
192196
<div>{{ x && (y ?? z) }}</div>
193197
<div>{{ x?.y ?? y?.z }}</div>
198+
<div>{{ (x?.y ?? y) || z }}</div>
199+
<div>{{ (x?.y ?? y) && z }}</div>
200+
<div>{{ z || (x?.y ?? y) }}</div>
201+
<div>{{ z && (x?.y ?? y) }}</div>
194202
`,
195203
}]
196204
}] });

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_parens.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {Component} from '@angular/core';
66
<div>{{ (x && y) ?? z }}</div>
77
<div>{{ x && (y ?? z) }}</div>
88
<div>{{ x?.y ?? y?.z }}</div>
9+
<div>{{ (x?.y ?? y) || z }}</div>
10+
<div>{{ (x?.y ?? y) && z }}</div>
11+
<div>{{ z || (x?.y ?? y) }}</div>
12+
<div>{{ z && (x?.y ?? y) }}</div>
913
`,
1014
})
1115
export class MyApp {

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_parens_template.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,12 @@ if (rf & 2) {
55
$i0$.ɵɵtextInterpolate(ctx.x && (ctx.y ?? ctx.z));
66
$i0$.ɵɵadvance(2);
77
$i0$.ɵɵtextInterpolate((ctx.x == null ? null : ctx.x.y) ?? (ctx.y == null ? null : ctx.y.z));
8+
$i0$.ɵɵadvance(2);
9+
$i0$.ɵɵtextInterpolate(((ctx.x == null ? null : ctx.x.y) ?? ctx.y) || ctx.z);
10+
$i0$.ɵɵadvance(2);
11+
$i0$.ɵɵtextInterpolate(((ctx.x == null ? null : ctx.x.y) ?? ctx.y) && ctx.z);
12+
$i0$.ɵɵadvance(2);
13+
$i0$.ɵɵtextInterpolate(ctx.z || ((ctx.x == null ? null : ctx.x.y) ?? ctx.y));
14+
$i0$.ɵɵadvance(2);
15+
$i0$.ɵɵtextInterpolate(ctx.z && ((ctx.x == null ? null : ctx.x.y) ?? ctx.y));
816
}

packages/compiler/src/template/pipeline/src/phases/strip_nonrequired_parentheses.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ import type {CompilationJob} from '../compilation';
1818
*
1919
* 1. Unary operators in the base of an exponentiation expression. For example, `-2 ** 3` is not
2020
* valid JavaScript, but `(-2) ** 3` is.
21+
*
2122
* 2. When mixing nullish coalescing (`??`) and logical and/or operators (`&&`, `||`), we need
2223
* parentheses. For example, `a ?? b && c` is not valid JavaScript, but `a ?? (b && c)` is.
24+
* Note: Because of the outcome of https://github.com/microsoft/TypeScript/issues/62307
25+
* We need (for now) to keep parentheses around the `??` operator when it is used with and/or operators.
26+
* For example, `a ?? b && c` is not valid JavaScript, but `(a ?? b) && c` is.
27+
*
2328
* 3. Ternary expression used as an operand for nullish coalescing. Typescript generates incorrect
2429
* code if the parentheses are missing. For example when `(a ? b : c) ?? d` is translated to
2530
* typescript AST, the parentheses node is removed, and then the remaining AST is printed, it
@@ -42,6 +47,11 @@ export function stripNonrequiredParentheses(job: CompilationJob): void {
4247
case o.BinaryOperator.NullishCoalesce:
4348
checkNullishCoalescingParens(expr, requiredParens);
4449
break;
50+
// these 2 cases can be dropped if the regression introduced in 5.9.2 is fixed
51+
// see https://github.com/microsoft/TypeScript/issues/62307
52+
case o.BinaryOperator.And:
53+
case o.BinaryOperator.Or:
54+
checkAndOrParens(expr, requiredParens);
4555
}
4656
}
4757
});
@@ -92,6 +102,16 @@ function checkNullishCoalescingParens(
92102
}
93103
}
94104

105+
function checkAndOrParens(expr: o.BinaryOperatorExpr, requiredParens: Set<o.ParenthesizedExpr>) {
106+
if (
107+
expr.lhs instanceof o.ParenthesizedExpr &&
108+
expr.lhs.expr instanceof o.BinaryOperatorExpr &&
109+
expr.lhs.expr.operator === o.BinaryOperator.NullishCoalesce
110+
) {
111+
requiredParens.add(expr.lhs);
112+
}
113+
}
114+
95115
function isLogicalAndOr(expr: o.Expression) {
96116
return (
97117
expr instanceof o.BinaryOperatorExpr &&

0 commit comments

Comments
 (0)