Skip to content

Improve typing of && operator with --strictNullChecks #8949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 22 additions & 28 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2844,7 +2844,7 @@ namespace ts {
}
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
// undefined from the final type.
if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return type;
Expand Down Expand Up @@ -2887,7 +2887,7 @@ namespace ts {
}

function addOptionality(type: Type, optional: boolean): Type {
return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type;
return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type;
}

// Return the inferred type for a variable, parameter, or property declaration
Expand Down Expand Up @@ -3222,7 +3222,7 @@ namespace ts {
if (!links.type) {
const type = createObjectType(TypeFlags.Anonymous, symbol);
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
addNullableKind(type, TypeFlags.Undefined) : type;
addTypeKind(type, TypeFlags.Undefined) : type;
}
return links.type;
}
Expand Down Expand Up @@ -6746,7 +6746,7 @@ namespace ts {
return getUnionType(types);
}
const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined);
return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
}

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

function getNullableKind(type: Type): TypeFlags {
let flags = type.flags;
if (flags & TypeFlags.Union) {
for (const t of (type as UnionType).types) {
flags |= t.flags;
}
}
return flags & TypeFlags.Nullable;
function getCombinedTypeFlags(type: Type): TypeFlags {
return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((<UnionType>type).types) : type.flags;
}

function addNullableKind(type: Type, kind: TypeFlags): Type {
if ((getNullableKind(type) & kind) !== kind) {
const types = [type];
if (kind & TypeFlags.Undefined) {
types.push(undefinedType);
}
if (kind & TypeFlags.Null) {
types.push(nullType);
}
type = getUnionType(types);
function addTypeKind(type: Type, kind: TypeFlags) {
if ((getCombinedTypeFlags(type) & kind) === kind) {
return type;
}
return type;
const types = [type];
if (kind & TypeFlags.String) types.push(stringType);
if (kind & TypeFlags.Number) types.push(numberType);
if (kind & TypeFlags.Boolean) types.push(booleanType);
if (kind & TypeFlags.Void) types.push(voidType);
if (kind & TypeFlags.Undefined) types.push(undefinedType);
if (kind & TypeFlags.Null) types.push(nullType);
return getUnionType(types);
}

function getNonNullableType(type: Type): Type {
Expand Down Expand Up @@ -7667,7 +7661,7 @@ namespace ts {
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined);
const visitedFlowStart = visitedFlowCount;
const result = getTypeAtFlowNode(reference.flowNode);
visitedFlowCount = visitedFlowStart;
Expand Down Expand Up @@ -8163,7 +8157,7 @@ namespace ts {
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
return type;
Expand Down Expand Up @@ -9945,7 +9939,7 @@ namespace ts {
function checkNonNullExpression(node: Expression | QualifiedName) {
const type = checkExpression(node);
if (strictNullChecks) {
const kind = getNullableKind(type);
const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable;
if (kind) {
error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
Diagnostics.Object_is_possibly_null_or_undefined :
Expand Down Expand Up @@ -11485,7 +11479,7 @@ namespace ts {
if (strictNullChecks) {
const declaration = symbol.valueDeclaration;
if (declaration && (<VariableLikeDeclaration>declaration).initializer) {
return addNullableKind(type, TypeFlags.Undefined);
return addTypeKind(type, TypeFlags.Undefined);
}
}
return type;
Expand Down Expand Up @@ -12411,7 +12405,7 @@ namespace ts {
case SyntaxKind.InKeyword:
return checkInExpression(left, right, leftType, rightType);
case SyntaxKind.AmpersandAmpersandToken:
return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType;
return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType;
case SyntaxKind.BarBarToken:
return getUnionType([getNonNullableType(leftType), rightType]);
case SyntaxKind.EqualsToken:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2209,6 +2209,7 @@ namespace ts {

/* @internal */
Nullable = Undefined | Null,
Falsy = String | Number | Boolean | Void | Undefined | Null,
/* @internal */
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never,
/* @internal */
Expand Down
156 changes: 156 additions & 0 deletions tests/baselines/reference/logicalAndOperatorStrictMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//// [logicalAndOperatorStrictMode.ts]

const a = [0];
const s = "";
const x = 0;
const b = false;
const v: void = undefined;
const u = undefined;
const n = null;
const z = s || x || u;

const a1 = a && a;
const a2 = a && s;
const a3 = a && x;
const a4 = a && b;
const a5 = a && v;
const a6 = a && u;
const a7 = a && n;
const a8 = a && z;

const s1 = s && a;
const s2 = s && s;
const s3 = s && x;
const s4 = s && b;
const s5 = s && v;
const s6 = s && u;
const s7 = s && n;
const s8 = s && z;

const x1 = x && a;
const x2 = x && s;
const x3 = x && x;
const x4 = x && b;
const x5 = x && v;
const x6 = x && u;
const x7 = x && n;
const x8 = x && z;

const b1 = b && a;
const b2 = b && s;
const b3 = b && x;
const b4 = b && b;
const b5 = b && v;
const b6 = b && u;
const b7 = b && n;
const b8 = b && z;

const v1 = v && a;
const v2 = v && s;
const v3 = v && x;
const v4 = v && b;
const v5 = v && v;
const v6 = v && u;
const v7 = v && n;
const v8 = v && z;

const u1 = u && a;
const u2 = u && s;
const u3 = u && x;
const u4 = u && b;
const u5 = u && v;
const u6 = u && u;
const u7 = u && n;
const u8 = u && z;

const n1 = n && a;
const n2 = n && s;
const n3 = n && x;
const n4 = n && b;
const n5 = n && v;
const n6 = n && u;
const n7 = n && n;
const n8 = n && z;

const z1 = z && a;
const z2 = z && s;
const z3 = z && x;
const z4 = z && b;
const z5 = z && v;
const z6 = z && u;
const z7 = z && n;
const z8 = z && z;

//// [logicalAndOperatorStrictMode.js]
var a = [0];
var s = "";
var x = 0;
var b = false;
var v = undefined;
var u = undefined;
var n = null;
var z = s || x || u;
var a1 = a && a;
var a2 = a && s;
var a3 = a && x;
var a4 = a && b;
var a5 = a && v;
var a6 = a && u;
var a7 = a && n;
var a8 = a && z;
var s1 = s && a;
var s2 = s && s;
var s3 = s && x;
var s4 = s && b;
var s5 = s && v;
var s6 = s && u;
var s7 = s && n;
var s8 = s && z;
var x1 = x && a;
var x2 = x && s;
var x3 = x && x;
var x4 = x && b;
var x5 = x && v;
var x6 = x && u;
var x7 = x && n;
var x8 = x && z;
var b1 = b && a;
var b2 = b && s;
var b3 = b && x;
var b4 = b && b;
var b5 = b && v;
var b6 = b && u;
var b7 = b && n;
var b8 = b && z;
var v1 = v && a;
var v2 = v && s;
var v3 = v && x;
var v4 = v && b;
var v5 = v && v;
var v6 = v && u;
var v7 = v && n;
var v8 = v && z;
var u1 = u && a;
var u2 = u && s;
var u3 = u && x;
var u4 = u && b;
var u5 = u && v;
var u6 = u && u;
var u7 = u && n;
var u8 = u && z;
var n1 = n && a;
var n2 = n && s;
var n3 = n && x;
var n4 = n && b;
var n5 = n && v;
var n6 = n && u;
var n7 = n && n;
var n8 = n && z;
var z1 = z && a;
var z2 = z && s;
var z3 = z && x;
var z4 = z && b;
var z5 = z && v;
var z6 = z && u;
var z7 = z && n;
var z8 = z && z;
Loading