Skip to content

Commit aeb1d97

Browse files
committed
Merge pull request #8949 from Microsoft/strictNullLogicalAnd
Improve typing of && operator with --strictNullChecks
2 parents 454ab50 + 13698a9 commit aeb1d97

File tree

6 files changed

+1035
-28
lines changed

6 files changed

+1035
-28
lines changed

src/compiler/checker.ts

+22-28
Original file line numberDiff line numberDiff line change
@@ -2844,7 +2844,7 @@ namespace ts {
28442844
}
28452845
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
28462846
// undefined from the final type.
2847-
if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
2847+
if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
28482848
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
28492849
}
28502850
return type;
@@ -2887,7 +2887,7 @@ namespace ts {
28872887
}
28882888

28892889
function addOptionality(type: Type, optional: boolean): Type {
2890-
return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type;
2890+
return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type;
28912891
}
28922892

28932893
// Return the inferred type for a variable, parameter, or property declaration
@@ -3222,7 +3222,7 @@ namespace ts {
32223222
if (!links.type) {
32233223
const type = createObjectType(TypeFlags.Anonymous, symbol);
32243224
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3225-
addNullableKind(type, TypeFlags.Undefined) : type;
3225+
addTypeKind(type, TypeFlags.Undefined) : type;
32263226
}
32273227
return links.type;
32283228
}
@@ -6746,7 +6746,7 @@ namespace ts {
67466746
return getUnionType(types);
67476747
}
67486748
const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined);
6749-
return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
6749+
return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
67506750
}
67516751

67526752
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
@@ -6817,28 +6817,22 @@ namespace ts {
68176817
return !!(type.flags & TypeFlags.Tuple);
68186818
}
68196819

6820-
function getNullableKind(type: Type): TypeFlags {
6821-
let flags = type.flags;
6822-
if (flags & TypeFlags.Union) {
6823-
for (const t of (type as UnionType).types) {
6824-
flags |= t.flags;
6825-
}
6826-
}
6827-
return flags & TypeFlags.Nullable;
6820+
function getCombinedTypeFlags(type: Type): TypeFlags {
6821+
return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((<UnionType>type).types) : type.flags;
68286822
}
68296823

6830-
function addNullableKind(type: Type, kind: TypeFlags): Type {
6831-
if ((getNullableKind(type) & kind) !== kind) {
6832-
const types = [type];
6833-
if (kind & TypeFlags.Undefined) {
6834-
types.push(undefinedType);
6835-
}
6836-
if (kind & TypeFlags.Null) {
6837-
types.push(nullType);
6838-
}
6839-
type = getUnionType(types);
6824+
function addTypeKind(type: Type, kind: TypeFlags) {
6825+
if ((getCombinedTypeFlags(type) & kind) === kind) {
6826+
return type;
68406827
}
6841-
return type;
6828+
const types = [type];
6829+
if (kind & TypeFlags.String) types.push(stringType);
6830+
if (kind & TypeFlags.Number) types.push(numberType);
6831+
if (kind & TypeFlags.Boolean) types.push(booleanType);
6832+
if (kind & TypeFlags.Void) types.push(voidType);
6833+
if (kind & TypeFlags.Undefined) types.push(undefinedType);
6834+
if (kind & TypeFlags.Null) types.push(nullType);
6835+
return getUnionType(types);
68426836
}
68436837

68446838
function getNonNullableType(type: Type): Type {
@@ -7667,7 +7661,7 @@ namespace ts {
76677661
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
76687662
return declaredType;
76697663
}
7670-
const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
7664+
const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined);
76717665
const visitedFlowStart = visitedFlowCount;
76727666
const result = getTypeAtFlowNode(reference.flowNode);
76737667
visitedFlowCount = visitedFlowStart;
@@ -8172,7 +8166,7 @@ namespace ts {
81728166
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
81738167
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
81748168
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
8175-
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
8169+
if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) {
81768170
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
81778171
// Return the declared type to reduce follow-on errors
81788172
return type;
@@ -9954,7 +9948,7 @@ namespace ts {
99549948
function checkNonNullExpression(node: Expression | QualifiedName) {
99559949
const type = checkExpression(node);
99569950
if (strictNullChecks) {
9957-
const kind = getNullableKind(type);
9951+
const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable;
99589952
if (kind) {
99599953
error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
99609954
Diagnostics.Object_is_possibly_null_or_undefined :
@@ -11488,7 +11482,7 @@ namespace ts {
1148811482
if (strictNullChecks) {
1148911483
const declaration = symbol.valueDeclaration;
1149011484
if (declaration && (<VariableLikeDeclaration>declaration).initializer) {
11491-
return addNullableKind(type, TypeFlags.Undefined);
11485+
return addTypeKind(type, TypeFlags.Undefined);
1149211486
}
1149311487
}
1149411488
return type;
@@ -12414,7 +12408,7 @@ namespace ts {
1241412408
case SyntaxKind.InKeyword:
1241512409
return checkInExpression(left, right, leftType, rightType);
1241612410
case SyntaxKind.AmpersandAmpersandToken:
12417-
return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType;
12411+
return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType;
1241812412
case SyntaxKind.BarBarToken:
1241912413
return getUnionType([getNonNullableType(leftType), rightType]);
1242012414
case SyntaxKind.EqualsToken:

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,7 @@ namespace ts {
22092209

22102210
/* @internal */
22112211
Nullable = Undefined | Null,
2212+
Falsy = String | Number | Boolean | Void | Undefined | Null,
22122213
/* @internal */
22132214
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never,
22142215
/* @internal */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
//// [logicalAndOperatorStrictMode.ts]
2+
3+
const a = [0];
4+
const s = "";
5+
const x = 0;
6+
const b = false;
7+
const v: void = undefined;
8+
const u = undefined;
9+
const n = null;
10+
const z = s || x || u;
11+
12+
const a1 = a && a;
13+
const a2 = a && s;
14+
const a3 = a && x;
15+
const a4 = a && b;
16+
const a5 = a && v;
17+
const a6 = a && u;
18+
const a7 = a && n;
19+
const a8 = a && z;
20+
21+
const s1 = s && a;
22+
const s2 = s && s;
23+
const s3 = s && x;
24+
const s4 = s && b;
25+
const s5 = s && v;
26+
const s6 = s && u;
27+
const s7 = s && n;
28+
const s8 = s && z;
29+
30+
const x1 = x && a;
31+
const x2 = x && s;
32+
const x3 = x && x;
33+
const x4 = x && b;
34+
const x5 = x && v;
35+
const x6 = x && u;
36+
const x7 = x && n;
37+
const x8 = x && z;
38+
39+
const b1 = b && a;
40+
const b2 = b && s;
41+
const b3 = b && x;
42+
const b4 = b && b;
43+
const b5 = b && v;
44+
const b6 = b && u;
45+
const b7 = b && n;
46+
const b8 = b && z;
47+
48+
const v1 = v && a;
49+
const v2 = v && s;
50+
const v3 = v && x;
51+
const v4 = v && b;
52+
const v5 = v && v;
53+
const v6 = v && u;
54+
const v7 = v && n;
55+
const v8 = v && z;
56+
57+
const u1 = u && a;
58+
const u2 = u && s;
59+
const u3 = u && x;
60+
const u4 = u && b;
61+
const u5 = u && v;
62+
const u6 = u && u;
63+
const u7 = u && n;
64+
const u8 = u && z;
65+
66+
const n1 = n && a;
67+
const n2 = n && s;
68+
const n3 = n && x;
69+
const n4 = n && b;
70+
const n5 = n && v;
71+
const n6 = n && u;
72+
const n7 = n && n;
73+
const n8 = n && z;
74+
75+
const z1 = z && a;
76+
const z2 = z && s;
77+
const z3 = z && x;
78+
const z4 = z && b;
79+
const z5 = z && v;
80+
const z6 = z && u;
81+
const z7 = z && n;
82+
const z8 = z && z;
83+
84+
//// [logicalAndOperatorStrictMode.js]
85+
var a = [0];
86+
var s = "";
87+
var x = 0;
88+
var b = false;
89+
var v = undefined;
90+
var u = undefined;
91+
var n = null;
92+
var z = s || x || u;
93+
var a1 = a && a;
94+
var a2 = a && s;
95+
var a3 = a && x;
96+
var a4 = a && b;
97+
var a5 = a && v;
98+
var a6 = a && u;
99+
var a7 = a && n;
100+
var a8 = a && z;
101+
var s1 = s && a;
102+
var s2 = s && s;
103+
var s3 = s && x;
104+
var s4 = s && b;
105+
var s5 = s && v;
106+
var s6 = s && u;
107+
var s7 = s && n;
108+
var s8 = s && z;
109+
var x1 = x && a;
110+
var x2 = x && s;
111+
var x3 = x && x;
112+
var x4 = x && b;
113+
var x5 = x && v;
114+
var x6 = x && u;
115+
var x7 = x && n;
116+
var x8 = x && z;
117+
var b1 = b && a;
118+
var b2 = b && s;
119+
var b3 = b && x;
120+
var b4 = b && b;
121+
var b5 = b && v;
122+
var b6 = b && u;
123+
var b7 = b && n;
124+
var b8 = b && z;
125+
var v1 = v && a;
126+
var v2 = v && s;
127+
var v3 = v && x;
128+
var v4 = v && b;
129+
var v5 = v && v;
130+
var v6 = v && u;
131+
var v7 = v && n;
132+
var v8 = v && z;
133+
var u1 = u && a;
134+
var u2 = u && s;
135+
var u3 = u && x;
136+
var u4 = u && b;
137+
var u5 = u && v;
138+
var u6 = u && u;
139+
var u7 = u && n;
140+
var u8 = u && z;
141+
var n1 = n && a;
142+
var n2 = n && s;
143+
var n3 = n && x;
144+
var n4 = n && b;
145+
var n5 = n && v;
146+
var n6 = n && u;
147+
var n7 = n && n;
148+
var n8 = n && z;
149+
var z1 = z && a;
150+
var z2 = z && s;
151+
var z3 = z && x;
152+
var z4 = z && b;
153+
var z5 = z && v;
154+
var z6 = z && u;
155+
var z7 = z && n;
156+
var z8 = z && z;

0 commit comments

Comments
 (0)