Skip to content

Commit d288ece

Browse files
committed
Extend type guards to filter objects by literal type properties :
type Name = { kind : "name"; name : string }; type Id = { kind : "id"; id : number }; type Obj = Name | Id; var o : obj; if (o.kind === "name") { /* o is considered as Name */ } else { /* o is considered as Obj - Name */ }
1 parent 8774828 commit d288ece

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

src/compiler/checker.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -4196,6 +4196,14 @@ module ts {
41964196
return type;
41974197
}
41984198

4199+
function keepAssignableTypes(type : Type, targetType : Type, assumeAssignable: boolean): Type {
4200+
if(type.flags & TypeFlags.Union) {
4201+
var types = (<UnionType>type).types;
4202+
return getUnionType(filter(types, t => assumeAssignable ? isTypeAssignableTo(t, targetType) : !isTypeAssignableTo(t, targetType)));
4203+
}
4204+
return type;
4205+
}
4206+
41994207
// Check if a given variable is assigned within a given syntax node
42004208
function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean {
42014209
var links = getNodeLinks(node);
@@ -4347,6 +4355,28 @@ module ts {
43474355
}
43484356
}
43494357

4358+
function narrowPropTypeByStringTypeEquality(type : Type, expr: BinaryExpression, assumeTrue: boolean): Type {
4359+
var left = <PropertyAccess>expr.left;
4360+
var right = expr.right;
4361+
var right_t = checkExpression(right);
4362+
if (left.kind !== SyntaxKind.PropertyAccess || left.left.kind !== SyntaxKind.Identifier ||
4363+
!(right_t.flags & TypeFlags.StringLiteral) ||
4364+
getResolvedSymbol(<Identifier>left.left) !== symbol) {
4365+
return type;
4366+
}
4367+
var t = checkPropertyAccess(left);
4368+
var smallerType = t;
4369+
if (isTypeAssignableTo(right_t, t)) {
4370+
smallerType = right_t;
4371+
}
4372+
var dummyProperties: SymbolTable = {};
4373+
var dummyProperty = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, left.right.text);
4374+
dummyProperty.type = smallerType;
4375+
dummyProperties[dummyProperty.name] = dummyProperty;
4376+
var dummyType = createAnonymousType(undefined, dummyProperties, emptyArray, emptyArray, undefined, undefined);
4377+
return keepAssignableTypes(type, dummyType, assumeTrue);
4378+
}
4379+
43504380
function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
43514381
if (assumeTrue) {
43524382
// The assumed result is true, therefore we narrow assuming each operand to be true.
@@ -4404,7 +4434,11 @@ module ts {
44044434
case SyntaxKind.BinaryExpression:
44054435
var operator = (<BinaryExpression>expr).operator;
44064436
if (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
4407-
return narrowTypeByEquality(type, <BinaryExpression>expr, assumeTrue);
4437+
if((<BinaryExpression>expr).left.kind === SyntaxKind.PropertyAccess) {
4438+
return narrowPropTypeByStringTypeEquality(type, <BinaryExpression>expr, assumeTrue);
4439+
} else {
4440+
return narrowTypeByEquality(type, <BinaryExpression>expr, assumeTrue);
4441+
}
44084442
}
44094443
else if (operator === SyntaxKind.AmpersandAmpersandToken) {
44104444
return narrowTypeByAnd(type, <BinaryExpression>expr, assumeTrue);

0 commit comments

Comments
 (0)