Account for right operands & fix a weird error message for leftmost nullish literals in checkNullishCoalesceOperands (#59569)

This commit is contained in:
Chiri Vulpes 2025-02-22 11:17:08 +13:00 committed by GitHub
parent edc497bb2b
commit 2bed7feee8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 612 additions and 183 deletions

View File

@ -39847,24 +39847,44 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
grammarErrorOnNode(right, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(right.operatorToken.kind), tokenToString(operatorToken.kind));
}
const leftTarget = skipOuterExpressions(left, OuterExpressionKinds.All);
const nullishSemantics = getSyntacticNullishnessSemantics(leftTarget);
if (nullishSemantics !== PredicateSemantics.Sometimes) {
if (node.parent.kind === SyntaxKind.BinaryExpression) {
error(leftTarget, Diagnostics.This_binary_expression_is_never_nullish_Are_you_missing_parentheses);
}
else {
if (nullishSemantics === PredicateSemantics.Always) {
error(leftTarget, Diagnostics.This_expression_is_always_nullish);
}
else {
error(leftTarget, Diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish);
}
}
checkNullishCoalesceOperandLeft(node);
checkNullishCoalesceOperandRight(node);
}
}
function checkNullishCoalesceOperandLeft(node: BinaryExpression) {
const leftTarget = skipOuterExpressions(node.left, OuterExpressionKinds.All);
const nullishSemantics = getSyntacticNullishnessSemantics(leftTarget);
if (nullishSemantics !== PredicateSemantics.Sometimes) {
if (nullishSemantics === PredicateSemantics.Always) {
error(leftTarget, Diagnostics.This_expression_is_always_nullish);
}
else {
error(leftTarget, Diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish);
}
}
}
function checkNullishCoalesceOperandRight(node: BinaryExpression) {
const rightTarget = skipOuterExpressions(node.right, OuterExpressionKinds.All);
const nullishSemantics = getSyntacticNullishnessSemantics(rightTarget);
if (isNotWithinNullishCoalesceExpression(node)) {
return;
}
if (nullishSemantics === PredicateSemantics.Always) {
error(rightTarget, Diagnostics.This_expression_is_always_nullish);
}
else if (nullishSemantics === PredicateSemantics.Never) {
error(rightTarget, Diagnostics.This_expression_is_never_nullish);
}
}
function isNotWithinNullishCoalesceExpression(node: BinaryExpression) {
return !isBinaryExpression(node.parent) || node.parent.operatorToken.kind !== SyntaxKind.QuestionQuestionToken;
}
function getSyntacticNullishnessSemantics(node: Node): PredicateSemantics {
node = skipOuterExpressions(node);
switch (node.kind) {

View File

@ -3987,6 +3987,10 @@
"category": "Error",
"code": 2880
},
"This expression is never nullish.": {
"category": "Error",
"code": 2881
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View File

@ -1,39 +1,61 @@
predicateSemantics.ts(7,16): error TS2871: This expression is always nullish.
predicateSemantics.ts(10,16): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(26,12): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(27,12): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(28,12): error TS2871: This expression is always nullish.
predicateSemantics.ts(29,12): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(30,12): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(33,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(34,11): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(35,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(36,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(51,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(52,14): error TS2695: Left side of comma operator is unused and has no side effects.
predicateSemantics.ts(52,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(70,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(26,13): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(27,13): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(28,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(29,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(30,13): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(31,13): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(32,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(32,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(33,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(34,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(34,22): error TS2871: This expression is always nullish.
predicateSemantics.ts(36,20): error TS2871: This expression is always nullish.
predicateSemantics.ts(37,20): error TS2871: This expression is always nullish.
predicateSemantics.ts(38,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(39,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(40,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(40,29): error TS2871: This expression is always nullish.
predicateSemantics.ts(41,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(42,20): error TS2881: This expression is never nullish.
predicateSemantics.ts(43,21): error TS2881: This expression is never nullish.
predicateSemantics.ts(45,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(45,21): error TS2871: This expression is always nullish.
predicateSemantics.ts(45,29): error TS2871: This expression is always nullish.
predicateSemantics.ts(46,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(46,21): error TS2881: This expression is never nullish.
predicateSemantics.ts(47,13): error TS2871: This expression is always nullish.
predicateSemantics.ts(47,22): error TS2881: This expression is never nullish.
predicateSemantics.ts(50,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(51,11): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(52,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(53,8): error TS2872: This kind of expression is always truthy.
predicateSemantics.ts(70,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(71,14): error TS2695: Left side of comma operator is unused and has no side effects.
predicateSemantics.ts(71,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(89,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
predicateSemantics.ts(90,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
==== predicateSemantics.ts (16 errors) ====
declare let cond: any;
==== predicateSemantics.ts (38 errors) ====
declare let opt: number | undefined;
// OK: One or other operand is possibly nullish
const test1 = (cond ? undefined : 32) ?? "possibly reached";
const test1 = (opt ? undefined : 32) ?? "possibly reached";
// Not OK: Both operands nullish
const test2 = (cond ? undefined : null) ?? "always reached";
~~~~~~~~~~~~~~~~~~~~~~~
const test2 = (opt ? undefined : null) ?? "always reached";
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2871: This expression is always nullish.
// Not OK: Both operands non-nullish
const test3 = (cond ? 132 : 17) ?? "unreachable";
~~~~~~~~~~~~~~~
const test3 = (opt ? 132 : 17) ?? "unreachable";
~~~~~~~~~~~~~~
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
// Parens
const test4 = (cond ? (undefined) : (17)) ?? 42;
const test4 = (opt ? (undefined) : (17)) ?? 42;
// Should be OK (special case)
if (!!true) {
@ -46,21 +68,82 @@ predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable be
while (true) { }
while (false) { }
const p5 = {} ?? null;
~~
const p01 = {} ?? null;
~~
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
const p6 = 0 > 1 ?? null;
~~~~~
const p02 = 0 > 1 ?? null;
~~~~~
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
const p7 = null ?? null;
~~~~
const p03 = null ?? 1;
~~~~
!!! error TS2871: This expression is always nullish.
const p8 = (class foo { }) && null;
~~~~~~~~~~~~~~~
const p04 = null ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
const p05 = (class foo { }) && null;
~~~~~~~~~~~~~~~
!!! error TS2872: This kind of expression is always truthy.
const p9 = (class foo { }) || null;
~~~~~~~~~~~~~~~
const p06 = (class foo { }) || null;
~~~~~~~~~~~~~~~
!!! error TS2872: This kind of expression is always truthy.
const p07 = null ?? null ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
~~~~
!!! error TS2871: This expression is always nullish.
const p08 = null ?? opt ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
const p09 = null ?? (opt ? null : undefined) ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2871: This expression is always nullish.
const p10 = opt ?? null ?? 1;
~~~~
!!! error TS2871: This expression is always nullish.
const p11 = opt ?? null ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
const p12 = opt ?? (null ?? 1);
~~~~
!!! error TS2871: This expression is always nullish.
const p13 = opt ?? (null ?? null);
~~~~
!!! error TS2871: This expression is always nullish.
const p14 = opt ?? (null ?? null ?? null);
~~~~
!!! error TS2871: This expression is always nullish.
~~~~
!!! error TS2871: This expression is always nullish.
const p15 = opt ?? (opt ? null : undefined) ?? null;
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2871: This expression is always nullish.
const p16 = opt ?? 1 ?? 2;
~
!!! error TS2881: This expression is never nullish.
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
~~~~~~~~~~~
!!! error TS2881: This expression is never nullish.
const p21 = null ?? null ?? null ?? null;
~~~~
!!! error TS2871: This expression is always nullish.
~~~~
!!! error TS2871: This expression is always nullish.
~~~~
!!! error TS2871: This expression is always nullish.
const p22 = null ?? 1 ?? 1;
~~~~
!!! error TS2871: This expression is always nullish.
~
!!! error TS2881: This expression is never nullish.
const p23 = null ?? (opt ? 1 : 2) ?? 1;
~~~~
!!! error TS2871: This expression is always nullish.
~~~~~~~~~~~
!!! error TS2881: This expression is never nullish.
// Outer expression tests
while ({} as any) { }
@ -76,6 +159,8 @@ predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable be
~~~~~~
!!! error TS2872: This kind of expression is always truthy.
declare let cond: any;
// Should be OK
console.log((cond || undefined) && 1 / cond);

View File

@ -1,19 +1,19 @@
//// [tests/cases/compiler/predicateSemantics.ts] ////
//// [predicateSemantics.ts]
declare let cond: any;
declare let opt: number | undefined;
// OK: One or other operand is possibly nullish
const test1 = (cond ? undefined : 32) ?? "possibly reached";
const test1 = (opt ? undefined : 32) ?? "possibly reached";
// Not OK: Both operands nullish
const test2 = (cond ? undefined : null) ?? "always reached";
const test2 = (opt ? undefined : null) ?? "always reached";
// Not OK: Both operands non-nullish
const test3 = (cond ? 132 : 17) ?? "unreachable";
const test3 = (opt ? 132 : 17) ?? "unreachable";
// Parens
const test4 = (cond ? (undefined) : (17)) ?? 42;
const test4 = (opt ? (undefined) : (17)) ?? 42;
// Should be OK (special case)
if (!!true) {
@ -26,11 +26,28 @@ while (1) { }
while (true) { }
while (false) { }
const p5 = {} ?? null;
const p6 = 0 > 1 ?? null;
const p7 = null ?? null;
const p8 = (class foo { }) && null;
const p9 = (class foo { }) || null;
const p01 = {} ?? null;
const p02 = 0 > 1 ?? null;
const p03 = null ?? 1;
const p04 = null ?? null;
const p05 = (class foo { }) && null;
const p06 = (class foo { }) || null;
const p07 = null ?? null ?? null;
const p08 = null ?? opt ?? null;
const p09 = null ?? (opt ? null : undefined) ?? null;
const p10 = opt ?? null ?? 1;
const p11 = opt ?? null ?? null;
const p12 = opt ?? (null ?? 1);
const p13 = opt ?? (null ?? null);
const p14 = opt ?? (null ?? null ?? null);
const p15 = opt ?? (opt ? null : undefined) ?? null;
const p16 = opt ?? 1 ?? 2;
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
const p21 = null ?? null ?? null ?? null;
const p22 = null ?? 1 ?? 1;
const p23 = null ?? (opt ? 1 : 2) ?? 1;
// Outer expression tests
while ({} as any) { }
@ -38,6 +55,8 @@ while ({} satisfies unknown) { }
while ((<any>({}))) { }
while ((({}))) { }
declare let cond: any;
// Should be OK
console.log((cond || undefined) && 1 / cond);
@ -79,15 +98,15 @@ var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cook
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
// OK: One or other operand is possibly nullish
var test1 = (_a = (cond ? undefined : 32)) !== null && _a !== void 0 ? _a : "possibly reached";
var test1 = (_a = (opt ? undefined : 32)) !== null && _a !== void 0 ? _a : "possibly reached";
// Not OK: Both operands nullish
var test2 = (_b = (cond ? undefined : null)) !== null && _b !== void 0 ? _b : "always reached";
var test2 = (_b = (opt ? undefined : null)) !== null && _b !== void 0 ? _b : "always reached";
// Not OK: Both operands non-nullish
var test3 = (_c = (cond ? 132 : 17)) !== null && _c !== void 0 ? _c : "unreachable";
var test3 = (_c = (opt ? 132 : 17)) !== null && _c !== void 0 ? _c : "unreachable";
// Parens
var test4 = (_d = (cond ? (undefined) : (17))) !== null && _d !== void 0 ? _d : 42;
var test4 = (_d = (opt ? (undefined) : (17))) !== null && _d !== void 0 ? _d : 42;
// Should be OK (special case)
if (!!true) {
}
@ -96,19 +115,34 @@ while (0) { }
while (1) { }
while (true) { }
while (false) { }
var p5 = (_e = {}) !== null && _e !== void 0 ? _e : null;
var p6 = (_f = 0 > 1) !== null && _f !== void 0 ? _f : null;
var p7 = null !== null && null !== void 0 ? null : null;
var p8 = (/** @class */ (function () {
var p01 = (_e = {}) !== null && _e !== void 0 ? _e : null;
var p02 = (_f = 0 > 1) !== null && _f !== void 0 ? _f : null;
var p03 = null !== null && null !== void 0 ? null : 1;
var p04 = null !== null && null !== void 0 ? null : null;
var p05 = (/** @class */ (function () {
function foo() {
}
return foo;
}())) && null;
var p9 = (/** @class */ (function () {
var p06 = (/** @class */ (function () {
function foo() {
}
return foo;
}())) || null;
var p07 = (_g = null !== null && null !== void 0 ? null : null) !== null && _g !== void 0 ? _g : null;
var p08 = (_h = null !== null && null !== void 0 ? null : opt) !== null && _h !== void 0 ? _h : null;
var p09 = (_j = null !== null && null !== void 0 ? null : (opt ? null : undefined)) !== null && _j !== void 0 ? _j : null;
var p10 = (_k = opt !== null && opt !== void 0 ? opt : null) !== null && _k !== void 0 ? _k : 1;
var p11 = (_l = opt !== null && opt !== void 0 ? opt : null) !== null && _l !== void 0 ? _l : null;
var p12 = opt !== null && opt !== void 0 ? opt : (null !== null && null !== void 0 ? null : 1);
var p13 = opt !== null && opt !== void 0 ? opt : (null !== null && null !== void 0 ? null : null);
var p14 = opt !== null && opt !== void 0 ? opt : ((_m = null !== null && null !== void 0 ? null : null) !== null && _m !== void 0 ? _m : null);
var p15 = (_o = opt !== null && opt !== void 0 ? opt : (opt ? null : undefined)) !== null && _o !== void 0 ? _o : null;
var p16 = (_p = opt !== null && opt !== void 0 ? opt : 1) !== null && _p !== void 0 ? _p : 2;
var p17 = (_q = opt !== null && opt !== void 0 ? opt : (opt ? 1 : 2)) !== null && _q !== void 0 ? _q : 3;
var p21 = (_s = (_r = null !== null && null !== void 0 ? null : null) !== null && _r !== void 0 ? _r : null) !== null && _s !== void 0 ? _s : null;
var p22 = (_t = null !== null && null !== void 0 ? null : 1) !== null && _t !== void 0 ? _t : 1;
var p23 = (_u = null !== null && null !== void 0 ? null : (opt ? 1 : 2)) !== null && _u !== void 0 ? _u : 1;
// Outer expression tests
while ({}) { }
while ({}) { }
@ -124,9 +158,9 @@ function foo() {
{
var maybe = null;
var i = 0;
var d = (_g = (i++, maybe)) !== null && _g !== void 0 ? _g : true; // ok
var e = (_h = (i++, i++)) !== null && _h !== void 0 ? _h : true; // error
var f = (_j = (maybe, i++)) !== null && _j !== void 0 ? _j : true; // error
var d = (_v = (i++, maybe)) !== null && _v !== void 0 ? _v : true; // ok
var e = (_w = (i++, i++)) !== null && _w !== void 0 ? _w : true; // error
var f = (_x = (maybe, i++)) !== null && _x !== void 0 ? _x : true; // error
}
// https://github.com/microsoft/TypeScript/issues/60439
var X = /** @class */ (function () {
@ -137,6 +171,6 @@ var X = /** @class */ (function () {
}
return X;
}());
(_k = tag(__makeTemplateObject(["foo", ""], ["foo", ""]), 1)) !== null && _k !== void 0 ? _k : 32; // ok
(_l = "foo".concat(1)) !== null && _l !== void 0 ? _l : 32; // error
(_y = tag(__makeTemplateObject(["foo", ""], ["foo", ""]), 1)) !== null && _y !== void 0 ? _y : 32; // ok
(_z = "foo".concat(1)) !== null && _z !== void 0 ? _z : 32; // error
"foo" !== null && "foo" !== void 0 ? "foo" : 32; // error

View File

@ -1,30 +1,30 @@
//// [tests/cases/compiler/predicateSemantics.ts] ////
=== predicateSemantics.ts ===
declare let cond: any;
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
declare let opt: number | undefined;
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
// OK: One or other operand is possibly nullish
const test1 = (cond ? undefined : 32) ?? "possibly reached";
const test1 = (opt ? undefined : 32) ?? "possibly reached";
>test1 : Symbol(test1, Decl(predicateSemantics.ts, 3, 5))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>undefined : Symbol(undefined)
// Not OK: Both operands nullish
const test2 = (cond ? undefined : null) ?? "always reached";
const test2 = (opt ? undefined : null) ?? "always reached";
>test2 : Symbol(test2, Decl(predicateSemantics.ts, 6, 5))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>undefined : Symbol(undefined)
// Not OK: Both operands non-nullish
const test3 = (cond ? 132 : 17) ?? "unreachable";
const test3 = (opt ? 132 : 17) ?? "unreachable";
>test3 : Symbol(test3, Decl(predicateSemantics.ts, 9, 5))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
// Parens
const test4 = (cond ? (undefined) : (17)) ?? 42;
const test4 = (opt ? (undefined) : (17)) ?? 42;
>test4 : Symbol(test4, Decl(predicateSemantics.ts, 12, 5))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>undefined : Symbol(undefined)
// Should be OK (special case)
@ -38,22 +38,82 @@ while (1) { }
while (true) { }
while (false) { }
const p5 = {} ?? null;
>p5 : Symbol(p5, Decl(predicateSemantics.ts, 25, 5))
const p01 = {} ?? null;
>p01 : Symbol(p01, Decl(predicateSemantics.ts, 25, 5))
const p6 = 0 > 1 ?? null;
>p6 : Symbol(p6, Decl(predicateSemantics.ts, 26, 5))
const p02 = 0 > 1 ?? null;
>p02 : Symbol(p02, Decl(predicateSemantics.ts, 26, 5))
const p7 = null ?? null;
>p7 : Symbol(p7, Decl(predicateSemantics.ts, 27, 5))
const p03 = null ?? 1;
>p03 : Symbol(p03, Decl(predicateSemantics.ts, 27, 5))
const p8 = (class foo { }) && null;
>p8 : Symbol(p8, Decl(predicateSemantics.ts, 28, 5))
>foo : Symbol(foo, Decl(predicateSemantics.ts, 28, 12))
const p04 = null ?? null;
>p04 : Symbol(p04, Decl(predicateSemantics.ts, 28, 5))
const p9 = (class foo { }) || null;
>p9 : Symbol(p9, Decl(predicateSemantics.ts, 29, 5))
>foo : Symbol(foo, Decl(predicateSemantics.ts, 29, 12))
const p05 = (class foo { }) && null;
>p05 : Symbol(p05, Decl(predicateSemantics.ts, 29, 5))
>foo : Symbol(foo, Decl(predicateSemantics.ts, 29, 13))
const p06 = (class foo { }) || null;
>p06 : Symbol(p06, Decl(predicateSemantics.ts, 30, 5))
>foo : Symbol(foo, Decl(predicateSemantics.ts, 30, 13))
const p07 = null ?? null ?? null;
>p07 : Symbol(p07, Decl(predicateSemantics.ts, 31, 5))
const p08 = null ?? opt ?? null;
>p08 : Symbol(p08, Decl(predicateSemantics.ts, 32, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p09 = null ?? (opt ? null : undefined) ?? null;
>p09 : Symbol(p09, Decl(predicateSemantics.ts, 33, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>undefined : Symbol(undefined)
const p10 = opt ?? null ?? 1;
>p10 : Symbol(p10, Decl(predicateSemantics.ts, 35, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p11 = opt ?? null ?? null;
>p11 : Symbol(p11, Decl(predicateSemantics.ts, 36, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p12 = opt ?? (null ?? 1);
>p12 : Symbol(p12, Decl(predicateSemantics.ts, 37, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p13 = opt ?? (null ?? null);
>p13 : Symbol(p13, Decl(predicateSemantics.ts, 38, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p14 = opt ?? (null ?? null ?? null);
>p14 : Symbol(p14, Decl(predicateSemantics.ts, 39, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p15 = opt ?? (opt ? null : undefined) ?? null;
>p15 : Symbol(p15, Decl(predicateSemantics.ts, 40, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>undefined : Symbol(undefined)
const p16 = opt ?? 1 ?? 2;
>p16 : Symbol(p16, Decl(predicateSemantics.ts, 41, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
>p17 : Symbol(p17, Decl(predicateSemantics.ts, 42, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
const p21 = null ?? null ?? null ?? null;
>p21 : Symbol(p21, Decl(predicateSemantics.ts, 44, 5))
const p22 = null ?? 1 ?? 1;
>p22 : Symbol(p22, Decl(predicateSemantics.ts, 45, 5))
const p23 = null ?? (opt ? 1 : 2) ?? 1;
>p23 : Symbol(p23, Decl(predicateSemantics.ts, 46, 5))
>opt : Symbol(opt, Decl(predicateSemantics.ts, 0, 11))
// Outer expression tests
while ({} as any) { }
@ -61,78 +121,81 @@ while ({} satisfies unknown) { }
while ((<any>({}))) { }
while ((({}))) { }
declare let cond: any;
>cond : Symbol(cond, Decl(predicateSemantics.ts, 54, 11))
// Should be OK
console.log((cond || undefined) && 1 / cond);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 54, 11))
>undefined : Symbol(undefined)
>cond : Symbol(cond, Decl(predicateSemantics.ts, 0, 11))
>cond : Symbol(cond, Decl(predicateSemantics.ts, 54, 11))
function foo(this: Object | undefined) {
>foo : Symbol(foo, Decl(predicateSemantics.ts, 38, 45))
>this : Symbol(this, Decl(predicateSemantics.ts, 40, 13))
>foo : Symbol(foo, Decl(predicateSemantics.ts, 57, 45))
>this : Symbol(this, Decl(predicateSemantics.ts, 59, 13))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
// Should be OK
return this ?? 0;
>this : Symbol(this, Decl(predicateSemantics.ts, 40, 13))
>this : Symbol(this, Decl(predicateSemantics.ts, 59, 13))
}
// https://github.com/microsoft/TypeScript/issues/60401
{
const maybe = null as true | null;
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 47, 7))
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 66, 7))
let i = 0;
>i : Symbol(i, Decl(predicateSemantics.ts, 48, 5))
>i : Symbol(i, Decl(predicateSemantics.ts, 67, 5))
const d = (i++, maybe) ?? true; // ok
>d : Symbol(d, Decl(predicateSemantics.ts, 49, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 48, 5))
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 47, 7))
>d : Symbol(d, Decl(predicateSemantics.ts, 68, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 67, 5))
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 66, 7))
const e = (i++, i++) ?? true; // error
>e : Symbol(e, Decl(predicateSemantics.ts, 50, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 48, 5))
>i : Symbol(i, Decl(predicateSemantics.ts, 48, 5))
>e : Symbol(e, Decl(predicateSemantics.ts, 69, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 67, 5))
>i : Symbol(i, Decl(predicateSemantics.ts, 67, 5))
const f = (maybe, i++) ?? true; // error
>f : Symbol(f, Decl(predicateSemantics.ts, 51, 7))
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 47, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 48, 5))
>f : Symbol(f, Decl(predicateSemantics.ts, 70, 7))
>maybe : Symbol(maybe, Decl(predicateSemantics.ts, 66, 7))
>i : Symbol(i, Decl(predicateSemantics.ts, 67, 5))
}
// https://github.com/microsoft/TypeScript/issues/60439
class X {
>X : Symbol(X, Decl(predicateSemantics.ts, 52, 1))
>X : Symbol(X, Decl(predicateSemantics.ts, 71, 1))
constructor() {
const p = new.target ?? 32;
>p : Symbol(p, Decl(predicateSemantics.ts, 57, 9))
>new.target : Symbol(X, Decl(predicateSemantics.ts, 52, 1))
>target : Symbol(X, Decl(predicateSemantics.ts, 52, 1))
>p : Symbol(p, Decl(predicateSemantics.ts, 76, 9))
>new.target : Symbol(X, Decl(predicateSemantics.ts, 71, 1))
>target : Symbol(X, Decl(predicateSemantics.ts, 71, 1))
}
}
// https://github.com/microsoft/TypeScript/issues/60614
declare function tag<T>(
>tag : Symbol(tag, Decl(predicateSemantics.ts, 59, 1))
>T : Symbol(T, Decl(predicateSemantics.ts, 62, 21))
>tag : Symbol(tag, Decl(predicateSemantics.ts, 78, 1))
>T : Symbol(T, Decl(predicateSemantics.ts, 81, 21))
strings: TemplateStringsArray,
>strings : Symbol(strings, Decl(predicateSemantics.ts, 62, 24))
>strings : Symbol(strings, Decl(predicateSemantics.ts, 81, 24))
>TemplateStringsArray : Symbol(TemplateStringsArray, Decl(lib.es5.d.ts, --, --))
...values: number[]
>values : Symbol(values, Decl(predicateSemantics.ts, 63, 32))
>values : Symbol(values, Decl(predicateSemantics.ts, 82, 32))
): T | null;
>T : Symbol(T, Decl(predicateSemantics.ts, 62, 21))
>T : Symbol(T, Decl(predicateSemantics.ts, 81, 21))
tag`foo${1}` ?? 32; // ok
>tag : Symbol(tag, Decl(predicateSemantics.ts, 59, 1))
>tag : Symbol(tag, Decl(predicateSemantics.ts, 78, 1))
`foo${1}` ?? 32; // error
`foo` ?? 32; // error

View File

@ -1,22 +1,22 @@
//// [tests/cases/compiler/predicateSemantics.ts] ////
=== predicateSemantics.ts ===
declare let cond: any;
>cond : any
> : ^^^
declare let opt: number | undefined;
>opt : number
> : ^^^^^^
// OK: One or other operand is possibly nullish
const test1 = (cond ? undefined : 32) ?? "possibly reached";
const test1 = (opt ? undefined : 32) ?? "possibly reached";
>test1 : 32 | "possibly reached"
> : ^^^^^^^^^^^^^^^^^^^^^^^
>(cond ? undefined : 32) ?? "possibly reached" : 32 | "possibly reached"
> : ^^^^^^^^^^^^^^^^^^^^^^^
>(cond ? undefined : 32) : 32
> : ^^
>cond ? undefined : 32 : 32
> : ^^
>cond : any
> : ^^^
>(opt ? undefined : 32) ?? "possibly reached" : 32 | "possibly reached"
> : ^^^^^^^^^^^^^^^^^^^^^^^
>(opt ? undefined : 32) : 32
> : ^^
>opt ? undefined : 32 : 32
> : ^^
>opt : number
> : ^^^^^^
>undefined : undefined
> : ^^^^^^^^^
>32 : 32
@ -25,34 +25,34 @@ const test1 = (cond ? undefined : 32) ?? "possibly reached";
> : ^^^^^^^^^^^^^^^^^^
// Not OK: Both operands nullish
const test2 = (cond ? undefined : null) ?? "always reached";
const test2 = (opt ? undefined : null) ?? "always reached";
>test2 : "always reached"
> : ^^^^^^^^^^^^^^^^
>(cond ? undefined : null) ?? "always reached" : "always reached"
> : ^^^^^^^^^^^^^^^^
>(cond ? undefined : null) : null
> : ^^^^
>cond ? undefined : null : null
> : ^^^^
>cond : any
> : ^^^
>(opt ? undefined : null) ?? "always reached" : "always reached"
> : ^^^^^^^^^^^^^^^^
>(opt ? undefined : null) : null
> : ^^^^
>opt ? undefined : null : null
> : ^^^^
>opt : number
> : ^^^^^^
>undefined : undefined
> : ^^^^^^^^^
>"always reached" : "always reached"
> : ^^^^^^^^^^^^^^^^
// Not OK: Both operands non-nullish
const test3 = (cond ? 132 : 17) ?? "unreachable";
const test3 = (opt ? 132 : 17) ?? "unreachable";
>test3 : 132 | 17 | "unreachable"
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>(cond ? 132 : 17) ?? "unreachable" : 132 | 17 | "unreachable"
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>(cond ? 132 : 17) : 132 | 17
> : ^^^^^^^^
>cond ? 132 : 17 : 132 | 17
> : ^^^^^^^^
>cond : any
> : ^^^
>(opt ? 132 : 17) ?? "unreachable" : 132 | 17 | "unreachable"
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>(opt ? 132 : 17) : 132 | 17
> : ^^^^^^^^
>opt ? 132 : 17 : 132 | 17
> : ^^^^^^^^
>opt : number
> : ^^^^^^
>132 : 132
> : ^^^
>17 : 17
@ -61,17 +61,17 @@ const test3 = (cond ? 132 : 17) ?? "unreachable";
> : ^^^^^^^^^^^^^
// Parens
const test4 = (cond ? (undefined) : (17)) ?? 42;
const test4 = (opt ? (undefined) : (17)) ?? 42;
>test4 : 17 | 42
> : ^^^^^^^
>(cond ? (undefined) : (17)) ?? 42 : 17 | 42
> : ^^^^^^^
>(cond ? (undefined) : (17)) : 17
> : ^^
>cond ? (undefined) : (17) : 17
> : ^^
>cond : any
> : ^^^
>(opt ? (undefined) : (17)) ?? 42 : 17 | 42
> : ^^^^^^^
>(opt ? (undefined) : (17)) : 17
> : ^^
>opt ? (undefined) : (17) : 17
> : ^^
>opt : number
> : ^^^^^^
>(undefined) : undefined
> : ^^^^^^^^^
>undefined : undefined
@ -111,17 +111,17 @@ while (false) { }
>false : false
> : ^^^^^
const p5 = {} ?? null;
>p5 : {}
> : ^^
const p01 = {} ?? null;
>p01 : {}
> : ^^
>{} ?? null : {}
> : ^^
>{} : {}
> : ^^
const p6 = 0 > 1 ?? null;
>p6 : boolean
> : ^^^^^^^
const p02 = 0 > 1 ?? null;
>p02 : boolean
> : ^^^^^^^
>0 > 1 ?? null : boolean
> : ^^^^^^^
>0 > 1 : boolean
@ -131,15 +131,23 @@ const p6 = 0 > 1 ?? null;
>1 : 1
> : ^
const p7 = null ?? null;
>p7 : any
> : ^^^
const p03 = null ?? 1;
>p03 : 1
> : ^
>null ?? 1 : 1
> : ^
>1 : 1
> : ^
const p04 = null ?? null;
>p04 : any
> : ^^^
>null ?? null : null
> : ^^^^
const p8 = (class foo { }) && null;
>p8 : any
> : ^^^
const p05 = (class foo { }) && null;
>p05 : any
> : ^^^
>(class foo { }) && null : null
> : ^^^^
>(class foo { }) : typeof foo
@ -149,9 +157,9 @@ const p8 = (class foo { }) && null;
>foo : typeof foo
> : ^^^^^^^^^^
const p9 = (class foo { }) || null;
>p9 : typeof foo
> : ^^^^^^^^^^
const p06 = (class foo { }) || null;
>p06 : typeof foo
> : ^^^^^^^^^^
>(class foo { }) || null : typeof foo
> : ^^^^^^^^^^
>(class foo { }) : typeof foo
@ -161,6 +169,198 @@ const p9 = (class foo { }) || null;
>foo : typeof foo
> : ^^^^^^^^^^
const p07 = null ?? null ?? null;
>p07 : any
> : ^^^
>null ?? null ?? null : null
> : ^^^^
>null ?? null : null
> : ^^^^
const p08 = null ?? opt ?? null;
>p08 : number
> : ^^^^^^
>null ?? opt ?? null : number
> : ^^^^^^
>null ?? opt : number
> : ^^^^^^
>opt : number
> : ^^^^^^
const p09 = null ?? (opt ? null : undefined) ?? null;
>p09 : any
> : ^^^
>null ?? (opt ? null : undefined) ?? null : null
> : ^^^^
>null ?? (opt ? null : undefined) : null
> : ^^^^
>(opt ? null : undefined) : null
> : ^^^^
>opt ? null : undefined : null
> : ^^^^
>opt : number
> : ^^^^^^
>undefined : undefined
> : ^^^^^^^^^
const p10 = opt ?? null ?? 1;
>p10 : number
> : ^^^^^^
>opt ?? null ?? 1 : number
> : ^^^^^^
>opt ?? null : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>1 : 1
> : ^
const p11 = opt ?? null ?? null;
>p11 : number
> : ^^^^^^
>opt ?? null ?? null : number
> : ^^^^^^
>opt ?? null : number
> : ^^^^^^
>opt : number
> : ^^^^^^
const p12 = opt ?? (null ?? 1);
>p12 : number
> : ^^^^^^
>opt ?? (null ?? 1) : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>(null ?? 1) : 1
> : ^
>null ?? 1 : 1
> : ^
>1 : 1
> : ^
const p13 = opt ?? (null ?? null);
>p13 : number
> : ^^^^^^
>opt ?? (null ?? null) : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>(null ?? null) : null
> : ^^^^
>null ?? null : null
> : ^^^^
const p14 = opt ?? (null ?? null ?? null);
>p14 : number
> : ^^^^^^
>opt ?? (null ?? null ?? null) : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>(null ?? null ?? null) : null
> : ^^^^
>null ?? null ?? null : null
> : ^^^^
>null ?? null : null
> : ^^^^
const p15 = opt ?? (opt ? null : undefined) ?? null;
>p15 : number
> : ^^^^^^
>opt ?? (opt ? null : undefined) ?? null : number
> : ^^^^^^
>opt ?? (opt ? null : undefined) : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>(opt ? null : undefined) : null
> : ^^^^
>opt ? null : undefined : null
> : ^^^^
>opt : number
> : ^^^^^^
>undefined : undefined
> : ^^^^^^^^^
const p16 = opt ?? 1 ?? 2;
>p16 : number
> : ^^^^^^
>opt ?? 1 ?? 2 : number
> : ^^^^^^
>opt ?? 1 : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>1 : 1
> : ^
>2 : 2
> : ^
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
>p17 : number
> : ^^^^^^
>opt ?? (opt ? 1 : 2) ?? 3 : number
> : ^^^^^^
>opt ?? (opt ? 1 : 2) : number
> : ^^^^^^
>opt : number
> : ^^^^^^
>(opt ? 1 : 2) : 1 | 2
> : ^^^^^
>opt ? 1 : 2 : 1 | 2
> : ^^^^^
>opt : number
> : ^^^^^^
>1 : 1
> : ^
>2 : 2
> : ^
>3 : 3
> : ^
const p21 = null ?? null ?? null ?? null;
>p21 : any
> : ^^^
>null ?? null ?? null ?? null : null
> : ^^^^
>null ?? null ?? null : null
> : ^^^^
>null ?? null : null
> : ^^^^
const p22 = null ?? 1 ?? 1;
>p22 : 1
> : ^
>null ?? 1 ?? 1 : 1
> : ^
>null ?? 1 : 1
> : ^
>1 : 1
> : ^
>1 : 1
> : ^
const p23 = null ?? (opt ? 1 : 2) ?? 1;
>p23 : 1 | 2
> : ^^^^^
>null ?? (opt ? 1 : 2) ?? 1 : 1 | 2
> : ^^^^^
>null ?? (opt ? 1 : 2) : 1 | 2
> : ^^^^^
>(opt ? 1 : 2) : 1 | 2
> : ^^^^^
>opt ? 1 : 2 : 1 | 2
> : ^^^^^
>opt : number
> : ^^^^^^
>1 : 1
> : ^
>2 : 2
> : ^
>1 : 1
> : ^
// Outer expression tests
while ({} as any) { }
>{} as any : any
@ -192,6 +392,10 @@ while ((({}))) { }
>{} : {}
> : ^^
declare let cond: any;
>cond : any
> : ^^^
// Should be OK
console.log((cond || undefined) && 1 / cond);
>console.log((cond || undefined) && 1 / cond) : void

View File

@ -1,16 +1,16 @@
declare let cond: any;
declare let opt: number | undefined;
// OK: One or other operand is possibly nullish
const test1 = (cond ? undefined : 32) ?? "possibly reached";
const test1 = (opt ? undefined : 32) ?? "possibly reached";
// Not OK: Both operands nullish
const test2 = (cond ? undefined : null) ?? "always reached";
const test2 = (opt ? undefined : null) ?? "always reached";
// Not OK: Both operands non-nullish
const test3 = (cond ? 132 : 17) ?? "unreachable";
const test3 = (opt ? 132 : 17) ?? "unreachable";
// Parens
const test4 = (cond ? (undefined) : (17)) ?? 42;
const test4 = (opt ? (undefined) : (17)) ?? 42;
// Should be OK (special case)
if (!!true) {
@ -23,11 +23,28 @@ while (1) { }
while (true) { }
while (false) { }
const p5 = {} ?? null;
const p6 = 0 > 1 ?? null;
const p7 = null ?? null;
const p8 = (class foo { }) && null;
const p9 = (class foo { }) || null;
const p01 = {} ?? null;
const p02 = 0 > 1 ?? null;
const p03 = null ?? 1;
const p04 = null ?? null;
const p05 = (class foo { }) && null;
const p06 = (class foo { }) || null;
const p07 = null ?? null ?? null;
const p08 = null ?? opt ?? null;
const p09 = null ?? (opt ? null : undefined) ?? null;
const p10 = opt ?? null ?? 1;
const p11 = opt ?? null ?? null;
const p12 = opt ?? (null ?? 1);
const p13 = opt ?? (null ?? null);
const p14 = opt ?? (null ?? null ?? null);
const p15 = opt ?? (opt ? null : undefined) ?? null;
const p16 = opt ?? 1 ?? 2;
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
const p21 = null ?? null ?? null ?? null;
const p22 = null ?? 1 ?? 1;
const p23 = null ?? (opt ? 1 : 2) ?? 1;
// Outer expression tests
while ({} as any) { }
@ -35,6 +52,8 @@ while ({} satisfies unknown) { }
while ((<any>({}))) { }
while ((({}))) { }
declare let cond: any;
// Should be OK
console.log((cond || undefined) && 1 / cond);