Skip to content

Commit b33eeac

Browse files
committed
migrate nullish coalescing commit
1 parent dbabc12 commit b33eeac

16 files changed

+671
-5
lines changed

src/compiler/binder.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,8 @@ namespace ts {
934934
else {
935935
return node.kind === SyntaxKind.BinaryExpression && (
936936
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
937-
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken);
937+
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken ||
938+
(<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken);
938939
}
939940
}
940941
}
@@ -1367,7 +1368,7 @@ namespace ts {
13671368

13681369
function bindBinaryExpressionFlow(node: BinaryExpression) {
13691370
const operator = node.operatorToken.kind;
1370-
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
1371+
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) {
13711372
if (isTopLevelLogicalExpression(node)) {
13721373
const postExpressionLabel = createBranchLabel();
13731374
bindLogicalExpression(node, postExpressionLabel, postExpressionLabel);
@@ -3147,7 +3148,10 @@ namespace ts {
31473148
const operatorTokenKind = node.operatorToken.kind;
31483149
const leftKind = node.left.kind;
31493150

3150-
if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
3151+
if (operatorTokenKind === SyntaxKind.QuestionQuestionToken) {
3152+
transformFlags |= TransformFlags.AssertESNext;
3153+
}
3154+
else if (operatorTokenKind === SyntaxKind.EqualsToken && leftKind === SyntaxKind.ObjectLiteralExpression) {
31513155
// Destructuring object assignments with are ES2015 syntax
31523156
// and possibly ES2018 if they contain rest
31533157
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2015 | TransformFlags.AssertDestructuringAssignment;

src/compiler/checker.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11524,7 +11524,7 @@ namespace ts {
1152411524
return isContextSensitive((<ConditionalExpression>node).whenTrue) ||
1152511525
isContextSensitive((<ConditionalExpression>node).whenFalse);
1152611526
case SyntaxKind.BinaryExpression:
11527-
return (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken &&
11527+
return ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken || (<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) &&
1152811528
(isContextSensitive((<BinaryExpression>node).left) || isContextSensitive((<BinaryExpression>node).right));
1152911529
case SyntaxKind.PropertyAssignment:
1153011530
return isContextSensitive((<PropertyAssignment>node).initializer);
@@ -18477,6 +18477,7 @@ namespace ts {
1847718477
}
1847818478
return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive;
1847918479
case SyntaxKind.BarBarToken:
18480+
case SyntaxKind.QuestionQuestionToken:
1848018481
// When an || expression has a contextual type, the operands are contextually typed by that type, except
1848118482
// when that type originates in a binding pattern, the right operand is contextually typed by the type of
1848218483
// the left operand. When an || expression has no contextual type, the right operand is contextually typed
@@ -23761,7 +23762,7 @@ namespace ts {
2376123762
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword);
2376223763
}
2376323764
let leftType: Type;
23764-
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
23765+
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.QuestionQuestionToken) {
2376523766
leftType = checkTruthinessExpression(left, checkMode);
2376623767
}
2376723768
else {
@@ -23917,6 +23918,10 @@ namespace ts {
2391723918
return getTypeFacts(leftType) & TypeFacts.Falsy ?
2391823919
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) :
2391923920
leftType;
23921+
case SyntaxKind.QuestionQuestionToken:
23922+
return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ?
23923+
getUnionType([getNonNullableType(leftType), rightType]) :
23924+
leftType;
2392023925
case SyntaxKind.EqualsToken:
2392123926
const declKind = isBinaryExpression(left.parent) ? getAssignmentDeclarationKind(left.parent) : AssignmentDeclarationKind.None;
2392223927
checkAssignmentDeclaration(declKind, rightType);

src/compiler/factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,10 @@ namespace ts {
30443044
return createBinary(left, SyntaxKind.BarBarToken, right);
30453045
}
30463046

3047+
export function createNullishCoalescing(left: Expression, right: Expression) {
3048+
return createBinary(left, SyntaxKind.QuestionQuestionToken, right);
3049+
}
3050+
30473051
export function createLogicalNot(operand: Expression) {
30483052
return createPrefix(SyntaxKind.ExclamationToken, operand);
30493053
}

src/compiler/parser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4659,6 +4659,7 @@ namespace ts {
46594659
case SyntaxKind.ExclamationEqualsEqualsToken: // foo<x> !==
46604660
case SyntaxKind.AmpersandAmpersandToken: // foo<x> &&
46614661
case SyntaxKind.BarBarToken: // foo<x> ||
4662+
case SyntaxKind.QuestionQuestionToken: // foo<x> ??
46624663
case SyntaxKind.CaretToken: // foo<x> ^
46634664
case SyntaxKind.AmpersandToken: // foo<x> &
46644665
case SyntaxKind.BarToken: // foo<x> |

src/compiler/scanner.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ namespace ts {
182182
"&&": SyntaxKind.AmpersandAmpersandToken,
183183
"||": SyntaxKind.BarBarToken,
184184
"?": SyntaxKind.QuestionToken,
185+
"??": SyntaxKind.QuestionQuestionToken,
185186
":": SyntaxKind.ColonToken,
186187
"=": SyntaxKind.EqualsToken,
187188
"+=": SyntaxKind.PlusEqualsToken,
@@ -1778,6 +1779,9 @@ namespace ts {
17781779
pos++;
17791780
return token = SyntaxKind.GreaterThanToken;
17801781
case CharacterCodes.question:
1782+
if (text.charCodeAt(pos + 1) === CharacterCodes.question) {
1783+
return pos += 2, token = SyntaxKind.QuestionQuestionToken;
1784+
}
17811785
pos++;
17821786
return token = SyntaxKind.QuestionToken;
17831787
case CharacterCodes.openBracket:

src/compiler/transformers/esnext.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,41 @@ namespace ts {
1616
return node;
1717
}
1818
switch (node.kind) {
19+
case SyntaxKind.BinaryExpression:
20+
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.QuestionQuestionToken) {
21+
return transformNullishCoalescingExpression(<BinaryExpression>node)
22+
}
1923
default:
2024
return visitEachChild(node, visitor, context);
2125
}
2226
}
27+
28+
function createNotUndefinedCondition(node: Expression) {
29+
return isIdentifier(node) && !isGeneratedIdentifier(node)
30+
? createStrictInequality(createTypeOf(node), createLiteral("undefined"))
31+
: createStrictInequality(node, createVoidZero());
32+
}
33+
34+
function createNotNullCondition(node: Expression) {
35+
return createStrictInequality(node, createNull());
36+
}
37+
38+
function transformNullishCoalescingExpression(node: BinaryExpression) {
39+
const expressions: Expression[] = [];
40+
let left = visitNode(node.left, visitor, isExpression);
41+
if (!isIdentifier(left)) {
42+
const temp = createTempVariable(/*recordTempVariable*/ undefined);
43+
expressions.push(createAssignment(temp, left));
44+
left = temp;
45+
}
46+
expressions.push(
47+
createConditional(
48+
createLogicalAnd(
49+
createNotUndefinedCondition(left),
50+
createNotNullCondition(left)),
51+
left,
52+
visitNode(node.right, visitor, isExpression)));
53+
return inlineExpressions(expressions);
54+
}
2355
}
2456
}

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ namespace ts {
181181
QuestionToken,
182182
ColonToken,
183183
AtToken,
184+
QuestionQuestionToken,
184185
/** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */
185186
BacktickToken,
186187
// Assignments
@@ -1497,6 +1498,7 @@ namespace ts {
14971498
export type LogicalOperator
14981499
= SyntaxKind.AmpersandAmpersandToken
14991500
| SyntaxKind.BarBarToken
1501+
| SyntaxKind.QuestionQuestionToken
15001502
;
15011503

15021504
// see: https://tc39.github.io/ecma262/#prod-LogicalANDExpression

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2988,6 +2988,7 @@ namespace ts {
29882988

29892989
export function getBinaryOperatorPrecedence(kind: SyntaxKind): number {
29902990
switch (kind) {
2991+
case SyntaxKind.QuestionQuestionToken:
29912992
case SyntaxKind.BarBarToken:
29922993
return 5;
29932994
case SyntaxKind.AmpersandAmpersandToken:
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//// [nullishCoalescingOperator1.ts]
2+
declare const a1: string | undefined | null
3+
declare const a2: string | undefined | null
4+
declare const a3: string | undefined | null
5+
declare const a4: string | undefined | null
6+
7+
declare const b1: number | undefined | null
8+
declare const b2: number | undefined | null
9+
declare const b3: number | undefined | null
10+
declare const b4: number | undefined | null
11+
12+
declare const c1: boolean | undefined | null
13+
declare const c2: boolean | undefined | null
14+
declare const c3: boolean | undefined | null
15+
declare const c4: boolean | undefined | null
16+
17+
interface I { a: string }
18+
declare const d1: I | undefined | null
19+
declare const d2: I | undefined | null
20+
declare const d3: I | undefined | null
21+
declare const d4: I | undefined | null
22+
23+
const aa1 = a1 ?? 'whatever';
24+
const aa2 = a2 ?? 'whatever';
25+
const aa3 = a3 ?? 'whatever';
26+
const aa4 = a4 ?? 'whatever';
27+
28+
const bb1 = b1 ?? 1;
29+
const bb2 = b2 ?? 1;
30+
const bb3 = b3 ?? 1;
31+
const bb4 = b4 ?? 1;
32+
33+
const cc1 = c1 ?? true;
34+
const cc2 = c2 ?? true;
35+
const cc3 = c3 ?? true;
36+
const cc4 = c4 ?? true;
37+
38+
const dd1 = d1 ?? {b: 1};
39+
const dd2 = d2 ?? {b: 1};
40+
const dd3 = d3 ?? {b: 1};
41+
const dd4 = d4 ?? {b: 1};
42+
43+
//// [nullishCoalescingOperator1.js]
44+
"use strict";
45+
var aa1 = typeof a1 !== "undefined" && a1 !== null ? a1 : 'whatever';
46+
var aa2 = typeof a2 !== "undefined" && a2 !== null ? a2 : 'whatever';
47+
var aa3 = typeof a3 !== "undefined" && a3 !== null ? a3 : 'whatever';
48+
var aa4 = typeof a4 !== "undefined" && a4 !== null ? a4 : 'whatever';
49+
var bb1 = typeof b1 !== "undefined" && b1 !== null ? b1 : 1;
50+
var bb2 = typeof b2 !== "undefined" && b2 !== null ? b2 : 1;
51+
var bb3 = typeof b3 !== "undefined" && b3 !== null ? b3 : 1;
52+
var bb4 = typeof b4 !== "undefined" && b4 !== null ? b4 : 1;
53+
var cc1 = typeof c1 !== "undefined" && c1 !== null ? c1 : true;
54+
var cc2 = typeof c2 !== "undefined" && c2 !== null ? c2 : true;
55+
var cc3 = typeof c3 !== "undefined" && c3 !== null ? c3 : true;
56+
var cc4 = typeof c4 !== "undefined" && c4 !== null ? c4 : true;
57+
var dd1 = typeof d1 !== "undefined" && d1 !== null ? d1 : { b: 1 };
58+
var dd2 = typeof d2 !== "undefined" && d2 !== null ? d2 : { b: 1 };
59+
var dd3 = typeof d3 !== "undefined" && d3 !== null ? d3 : { b: 1 };
60+
var dd4 = typeof d4 !== "undefined" && d4 !== null ? d4 : { b: 1 };
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
=== tests/cases/conformance/expressions/nullishCoalescingOperator/nullishCoalescingOperator1.ts ===
2+
declare const a1: string | undefined | null
3+
>a1 : Symbol(a1, Decl(nullishCoalescingOperator1.ts, 0, 13))
4+
5+
declare const a2: string | undefined | null
6+
>a2 : Symbol(a2, Decl(nullishCoalescingOperator1.ts, 1, 13))
7+
8+
declare const a3: string | undefined | null
9+
>a3 : Symbol(a3, Decl(nullishCoalescingOperator1.ts, 2, 13))
10+
11+
declare const a4: string | undefined | null
12+
>a4 : Symbol(a4, Decl(nullishCoalescingOperator1.ts, 3, 13))
13+
14+
declare const b1: number | undefined | null
15+
>b1 : Symbol(b1, Decl(nullishCoalescingOperator1.ts, 5, 13))
16+
17+
declare const b2: number | undefined | null
18+
>b2 : Symbol(b2, Decl(nullishCoalescingOperator1.ts, 6, 13))
19+
20+
declare const b3: number | undefined | null
21+
>b3 : Symbol(b3, Decl(nullishCoalescingOperator1.ts, 7, 13))
22+
23+
declare const b4: number | undefined | null
24+
>b4 : Symbol(b4, Decl(nullishCoalescingOperator1.ts, 8, 13))
25+
26+
declare const c1: boolean | undefined | null
27+
>c1 : Symbol(c1, Decl(nullishCoalescingOperator1.ts, 10, 13))
28+
29+
declare const c2: boolean | undefined | null
30+
>c2 : Symbol(c2, Decl(nullishCoalescingOperator1.ts, 11, 13))
31+
32+
declare const c3: boolean | undefined | null
33+
>c3 : Symbol(c3, Decl(nullishCoalescingOperator1.ts, 12, 13))
34+
35+
declare const c4: boolean | undefined | null
36+
>c4 : Symbol(c4, Decl(nullishCoalescingOperator1.ts, 13, 13))
37+
38+
interface I { a: string }
39+
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
40+
>a : Symbol(I.a, Decl(nullishCoalescingOperator1.ts, 15, 13))
41+
42+
declare const d1: I | undefined | null
43+
>d1 : Symbol(d1, Decl(nullishCoalescingOperator1.ts, 16, 13))
44+
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
45+
46+
declare const d2: I | undefined | null
47+
>d2 : Symbol(d2, Decl(nullishCoalescingOperator1.ts, 17, 13))
48+
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
49+
50+
declare const d3: I | undefined | null
51+
>d3 : Symbol(d3, Decl(nullishCoalescingOperator1.ts, 18, 13))
52+
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
53+
54+
declare const d4: I | undefined | null
55+
>d4 : Symbol(d4, Decl(nullishCoalescingOperator1.ts, 19, 13))
56+
>I : Symbol(I, Decl(nullishCoalescingOperator1.ts, 13, 44))
57+
58+
const aa1 = a1 ?? 'whatever';
59+
>aa1 : Symbol(aa1, Decl(nullishCoalescingOperator1.ts, 21, 5))
60+
>a1 : Symbol(a1, Decl(nullishCoalescingOperator1.ts, 0, 13))
61+
62+
const aa2 = a2 ?? 'whatever';
63+
>aa2 : Symbol(aa2, Decl(nullishCoalescingOperator1.ts, 22, 5))
64+
>a2 : Symbol(a2, Decl(nullishCoalescingOperator1.ts, 1, 13))
65+
66+
const aa3 = a3 ?? 'whatever';
67+
>aa3 : Symbol(aa3, Decl(nullishCoalescingOperator1.ts, 23, 5))
68+
>a3 : Symbol(a3, Decl(nullishCoalescingOperator1.ts, 2, 13))
69+
70+
const aa4 = a4 ?? 'whatever';
71+
>aa4 : Symbol(aa4, Decl(nullishCoalescingOperator1.ts, 24, 5))
72+
>a4 : Symbol(a4, Decl(nullishCoalescingOperator1.ts, 3, 13))
73+
74+
const bb1 = b1 ?? 1;
75+
>bb1 : Symbol(bb1, Decl(nullishCoalescingOperator1.ts, 26, 5))
76+
>b1 : Symbol(b1, Decl(nullishCoalescingOperator1.ts, 5, 13))
77+
78+
const bb2 = b2 ?? 1;
79+
>bb2 : Symbol(bb2, Decl(nullishCoalescingOperator1.ts, 27, 5))
80+
>b2 : Symbol(b2, Decl(nullishCoalescingOperator1.ts, 6, 13))
81+
82+
const bb3 = b3 ?? 1;
83+
>bb3 : Symbol(bb3, Decl(nullishCoalescingOperator1.ts, 28, 5))
84+
>b3 : Symbol(b3, Decl(nullishCoalescingOperator1.ts, 7, 13))
85+
86+
const bb4 = b4 ?? 1;
87+
>bb4 : Symbol(bb4, Decl(nullishCoalescingOperator1.ts, 29, 5))
88+
>b4 : Symbol(b4, Decl(nullishCoalescingOperator1.ts, 8, 13))
89+
90+
const cc1 = c1 ?? true;
91+
>cc1 : Symbol(cc1, Decl(nullishCoalescingOperator1.ts, 31, 5))
92+
>c1 : Symbol(c1, Decl(nullishCoalescingOperator1.ts, 10, 13))
93+
94+
const cc2 = c2 ?? true;
95+
>cc2 : Symbol(cc2, Decl(nullishCoalescingOperator1.ts, 32, 5))
96+
>c2 : Symbol(c2, Decl(nullishCoalescingOperator1.ts, 11, 13))
97+
98+
const cc3 = c3 ?? true;
99+
>cc3 : Symbol(cc3, Decl(nullishCoalescingOperator1.ts, 33, 5))
100+
>c3 : Symbol(c3, Decl(nullishCoalescingOperator1.ts, 12, 13))
101+
102+
const cc4 = c4 ?? true;
103+
>cc4 : Symbol(cc4, Decl(nullishCoalescingOperator1.ts, 34, 5))
104+
>c4 : Symbol(c4, Decl(nullishCoalescingOperator1.ts, 13, 13))
105+
106+
const dd1 = d1 ?? {b: 1};
107+
>dd1 : Symbol(dd1, Decl(nullishCoalescingOperator1.ts, 36, 5))
108+
>d1 : Symbol(d1, Decl(nullishCoalescingOperator1.ts, 16, 13))
109+
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 36, 19))
110+
111+
const dd2 = d2 ?? {b: 1};
112+
>dd2 : Symbol(dd2, Decl(nullishCoalescingOperator1.ts, 37, 5))
113+
>d2 : Symbol(d2, Decl(nullishCoalescingOperator1.ts, 17, 13))
114+
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 37, 19))
115+
116+
const dd3 = d3 ?? {b: 1};
117+
>dd3 : Symbol(dd3, Decl(nullishCoalescingOperator1.ts, 38, 5))
118+
>d3 : Symbol(d3, Decl(nullishCoalescingOperator1.ts, 18, 13))
119+
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 38, 19))
120+
121+
const dd4 = d4 ?? {b: 1};
122+
>dd4 : Symbol(dd4, Decl(nullishCoalescingOperator1.ts, 39, 5))
123+
>d4 : Symbol(d4, Decl(nullishCoalescingOperator1.ts, 19, 13))
124+
>b : Symbol(b, Decl(nullishCoalescingOperator1.ts, 39, 19))
125+

0 commit comments

Comments
 (0)