@@ -13254,7 +13254,7 @@ namespace ts {
13254
13254
return isContextSensitive((<ConditionalExpression>node).whenTrue) ||
13255
13255
isContextSensitive((<ConditionalExpression>node).whenFalse);
13256
13256
case SyntaxKind.BinaryExpression:
13257
- return (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken &&
13257
+ return (( <BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken || (<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) &&
13258
13258
(isContextSensitive((<BinaryExpression>node).left) || isContextSensitive((<BinaryExpression>node).right));
13259
13259
case SyntaxKind.PropertyAssignment:
13260
13260
return isContextSensitive((<PropertyAssignment>node).initializer);
@@ -19697,7 +19697,8 @@ namespace ts {
19697
19697
// will be a subtype or the same type as the argument.
19698
19698
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
19699
19699
// for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a`
19700
- if (isOptionalChainRoot(expr.parent)) {
19700
+ if (isOptionalChainRoot(expr.parent) ||
19701
+ isBinaryExpression(expr.parent) && expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken && expr.parent.left === expr) {
19701
19702
return narrowTypeByOptionality(type, expr, assumeTrue);
19702
19703
}
19703
19704
switch (expr.kind) {
@@ -20903,6 +20904,7 @@ namespace ts {
20903
20904
}
20904
20905
return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive;
20905
20906
case SyntaxKind.BarBarToken:
20907
+ case SyntaxKind.QuestionQuestionToken:
20906
20908
// When an || expression has a contextual type, the operands are contextually typed by that type, except
20907
20909
// when that type originates in a binding pattern, the right operand is contextually typed by the type of
20908
20910
// the left operand. When an || expression has no contextual type, the right operand is contextually typed
@@ -26471,16 +26473,29 @@ namespace ts {
26471
26473
if (isInJSFile(node) && getAssignedExpandoInitializer(node)) {
26472
26474
return checkExpression(node.right, checkMode);
26473
26475
}
26476
+ checkGrammarNullishCoalesceWithLogicalExpression(node);
26474
26477
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node);
26475
26478
}
26476
26479
26480
+ function checkGrammarNullishCoalesceWithLogicalExpression(node: BinaryExpression) {
26481
+ const { left, operatorToken, right } = node;
26482
+ if (operatorToken.kind === SyntaxKind.QuestionQuestionToken) {
26483
+ if (isBinaryExpression(left) && (left.operatorToken.kind === SyntaxKind.BarBarToken || left.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken)) {
26484
+ grammarErrorOnNode(left, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(left.operatorToken.kind), tokenToString(operatorToken.kind));
26485
+ }
26486
+ if (isBinaryExpression(right) && (right.operatorToken.kind === SyntaxKind.BarBarToken || right.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken)) {
26487
+ grammarErrorOnNode(right, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(right.operatorToken.kind), tokenToString(operatorToken.kind));
26488
+ }
26489
+ }
26490
+ }
26491
+
26477
26492
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node): Type {
26478
26493
const operator = operatorToken.kind;
26479
26494
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
26480
26495
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword);
26481
26496
}
26482
26497
let leftType: Type;
26483
- if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
26498
+ if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken ) {
26484
26499
leftType = checkTruthinessExpression(left, checkMode);
26485
26500
}
26486
26501
else {
@@ -26641,6 +26656,10 @@ namespace ts {
26641
26656
return getTypeFacts(leftType) & TypeFacts.Falsy ?
26642
26657
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) :
26643
26658
leftType;
26659
+ case SyntaxKind.QuestionQuestionToken:
26660
+ return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ?
26661
+ getUnionType([getNonNullableType(leftType), rightType], UnionReduction.Subtype) :
26662
+ leftType;
26644
26663
case SyntaxKind.EqualsToken:
26645
26664
const declKind = isBinaryExpression(left.parent) ? getAssignmentDeclarationKind(left.parent) : AssignmentDeclarationKind.None;
26646
26665
checkAssignmentDeclaration(declKind, rightType);
0 commit comments