From d288ece46155f550a99c0f22a0dacfb29848cb58 Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Fri, 31 Oct 2014 17:23:30 +0100 Subject: [PATCH] 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 */ } --- src/compiler/checker.ts | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 647564694fe35..4ff17db78c3df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4196,6 +4196,14 @@ module ts { return type; } + function keepAssignableTypes(type : Type, targetType : Type, assumeAssignable: boolean): Type { + if(type.flags & TypeFlags.Union) { + var types = (type).types; + return getUnionType(filter(types, t => assumeAssignable ? isTypeAssignableTo(t, targetType) : !isTypeAssignableTo(t, targetType))); + } + return type; + } + // Check if a given variable is assigned within a given syntax node function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean { var links = getNodeLinks(node); @@ -4347,6 +4355,28 @@ module ts { } } + function narrowPropTypeByStringTypeEquality(type : Type, expr: BinaryExpression, assumeTrue: boolean): Type { + var left = expr.left; + var right = expr.right; + var right_t = checkExpression(right); + if (left.kind !== SyntaxKind.PropertyAccess || left.left.kind !== SyntaxKind.Identifier || + !(right_t.flags & TypeFlags.StringLiteral) || + getResolvedSymbol(left.left) !== symbol) { + return type; + } + var t = checkPropertyAccess(left); + var smallerType = t; + if (isTypeAssignableTo(right_t, t)) { + smallerType = right_t; + } + var dummyProperties: SymbolTable = {}; + var dummyProperty = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, left.right.text); + dummyProperty.type = smallerType; + dummyProperties[dummyProperty.name] = dummyProperty; + var dummyType = createAnonymousType(undefined, dummyProperties, emptyArray, emptyArray, undefined, undefined); + return keepAssignableTypes(type, dummyType, assumeTrue); + } + function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { if (assumeTrue) { // The assumed result is true, therefore we narrow assuming each operand to be true. @@ -4404,7 +4434,11 @@ module ts { case SyntaxKind.BinaryExpression: var operator = (expr).operator; if (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { - return narrowTypeByEquality(type, expr, assumeTrue); + if((expr).left.kind === SyntaxKind.PropertyAccess) { + return narrowPropTypeByStringTypeEquality(type, expr, assumeTrue); + } else { + return narrowTypeByEquality(type, expr, assumeTrue); + } } else if (operator === SyntaxKind.AmpersandAmpersandToken) { return narrowTypeByAnd(type, expr, assumeTrue);