Skip to content

Commit 95a314a

Browse files
committed
Correct support for type guards with unions containing enums
1 parent 29776f4 commit 95a314a

File tree

1 file changed

+40
-13
lines changed

1 file changed

+40
-13
lines changed

src/compiler/checker.ts

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ module ts {
107107
var diagnostics: Diagnostic[] = [];
108108
var diagnosticsModified: boolean = false;
109109

110+
var primitiveTypeInfo: Map<{ type: Type; flags: TypeFlags }> = {
111+
"string": {
112+
type: stringType,
113+
flags: TypeFlags.StringLike
114+
},
115+
"number": {
116+
type: numberType,
117+
flags: TypeFlags.NumberLike
118+
},
119+
"boolean": {
120+
type: booleanType,
121+
flags: TypeFlags.Boolean
122+
}
123+
};
124+
110125
function addDiagnostic(diagnostic: Diagnostic) {
111126
diagnostics.push(diagnostic);
112127
diagnosticsModified = true;
@@ -4454,12 +4469,15 @@ module ts {
44544469
Debug.fail("should not get here");
44554470
}
44564471

4457-
// Remove one or more primitive types from a union type
4458-
function subtractPrimitiveTypes(type: Type, subtractMask: TypeFlags): Type {
4472+
// For a union type, remove all constituent types for which the given flags have the given state
4473+
function removeTypesFromUnionType(type: Type, maskFlags: TypeFlags, maskState: boolean): Type {
44594474
if (type.flags & TypeFlags.Union) {
44604475
var types = (<UnionType>type).types;
4461-
if (forEach(types, t => t.flags & subtractMask)) {
4462-
return getUnionType(filter(types, t => !(t.flags & subtractMask)));
4476+
if (forEach(types, t => !(t.flags & maskFlags) !== maskState)) {
4477+
var reducedType = getUnionType(filter(types, t => !(t.flags & maskFlags) === maskState));
4478+
if (reducedType !== emptyObjectType) {
4479+
return reducedType;
4480+
}
44634481
}
44644482
}
44654483
return type;
@@ -4635,8 +4653,8 @@ module ts {
46354653
// Stop at the first containing function or module declaration
46364654
break loop;
46374655
}
4638-
// Use narrowed type if it is a subtype and construct contains no assignments to variable
4639-
if (narrowedType !== type && isTypeSubtypeOf(narrowedType, type)) {
4656+
// Use narrowed type if construct contains no assignments to variable
4657+
if (narrowedType !== type) {
46404658
if (isVariableAssignedWithin(symbol, node)) {
46414659
break;
46424660
}
@@ -4656,20 +4674,29 @@ module ts {
46564674
if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>left.expression) !== symbol) {
46574675
return type;
46584676
}
4659-
var t = right.text;
4660-
var checkType: Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType;
4677+
var typeInfo = primitiveTypeInfo[right.text];
46614678
if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) {
46624679
assumeTrue = !assumeTrue;
46634680
}
46644681
if (assumeTrue) {
4665-
// The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can
4666-
// remove the primitive types from the narrowed type.
4667-
return checkType === emptyObjectType ? subtractPrimitiveTypes(type, TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean) : checkType;
4682+
// Assumed result is true. If check was not for a primitive type, remove all primitive types
4683+
if (!typeInfo) {
4684+
return removeTypesFromUnionType(type, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.Boolean, true);
4685+
}
4686+
// Check was for a primitive type, return that primitive type if it is a subtype
4687+
if (isTypeSubtypeOf(typeInfo.type, type)) {
4688+
return typeInfo.type;
4689+
}
4690+
// Otherwise, remove all types that aren't of the primitive type kind
4691+
return removeTypesFromUnionType(type, typeInfo.flags, false);
46684692
}
46694693
else {
4670-
// The assumed result is false. If check was for a primitive type we can remove that type from the narrowed type.
4694+
// Assumed result is false. If check was for a primitive type, remove that primitive type
4695+
if (typeInfo) {
4696+
return removeTypesFromUnionType(type, typeInfo.flags, true);
4697+
}
46714698
// Otherwise we don't have enough information to do anything.
4672-
return checkType === emptyObjectType ? type : subtractPrimitiveTypes(type, checkType.flags);
4699+
return type;
46734700
}
46744701
}
46754702

0 commit comments

Comments
 (0)