demo/typescript.js
function getNarrowedTypeOfSymbol(symbol, node) {
            var type = getTypeOfSymbol(symbol);
            // Only narrow when symbol is variable of type any or an object, union, or type parameter type
            if (node && symbol.flags & 3 /* Variable */) {
                if (isTypeAny(type) || type.flags & (80896 /* ObjectType */ | 16384 /* Union */ | 512 /* TypeParameter */)) {
                    loop: while (node.parent) {
                        var child = node;
                        node = node.parent;
                        var narrowedType = type;
                        switch (node.kind) {
                            case 196 /* IfStatement */:
                                // In a branch of an if statement, narrow based on controlling expression
                                if (child !== node.expression) {
                                    narrowedType = narrowType(type, node.expression, /*assumeTrue*/ child === node.thenStatement);
                                }
                                break;
                            case 182 /* ConditionalExpression */:
                                // In a branch of a conditional expression, narrow based on controlling condition
                                if (child !== node.condition) {
                                    narrowedType = narrowType(type, node.condition, /*assumeTrue*/ child === node.whenTrue);
                                }
                                break;
                            case 181 /* BinaryExpression */:
                                // In the right operand of an && or ||, narrow based on left operand
                                if (child === node.right) {
                                    if (node.operatorToken.kind === 51 /* AmpersandAmpersandToken */) {
                                        narrowedType = narrowType(type, node.left, /*assumeTrue*/ true);
                                    }
                                    else if (node.operatorToken.kind === 52 /* BarBarToken */) {
                                        narrowedType = narrowType(type, node.left, /*assumeTrue*/ false);
                                    }
                                }
                                break;
                            case 248 /* SourceFile */:
                            case 218 /* ModuleDeclaration */:
                            case 213 /* FunctionDeclaration */:
                            case 143 /* MethodDeclaration */:
                            case 142 /* MethodSignature */:
                            case 145 /* GetAccessor */:
                            case 146 /* SetAccessor */:
                            case 144 /* Constructor */:
                                // Stop at the first containing function or module declaration
                                break loop;
                        }
                        // Use narrowed type if construct contains no assignments to variable
                        if (narrowedType !== type) {
                            if (isVariableAssignedWithin(symbol, node)) {
                                break;
                            }
                            type = narrowedType;
                        }
                    }
                }
            }
            return type;
            function narrowTypeByEquality(type, expr, assumeTrue) {
                // Check that we have 'typeof <symbol>' on the left and string literal on the right
                if (expr.left.kind !== 176 /* TypeOfExpression */ || expr.right.kind !== 9 /* StringLiteral */) {
                    return type;
                }
                var left = expr.left;
                var right = expr.right;
                if (left.expression.kind !== 69 /* Identifier */ || getResolvedSymbol(left.expression) !== symbol) {
                    return type;
                }
                var typeInfo = primitiveTypeInfo[right.text];
                if (expr.operatorToken.kind === 33 /* ExclamationEqualsEqualsToken */) {
                    assumeTrue = !assumeTrue;
                }
                if (assumeTrue) {
                    // Assumed result is true. If check was not for a primitive type, remove all primitive types
                    if (!typeInfo) {
                        return removeTypesFromUnionType(type, /*typeKind*/ 258 /* StringLike */ | 132 /* NumberLike */ | 8 /* Boolean */ | 16777216 /* ESSymbol */,
                            /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ false);
                    }
                    // Check was for a primitive type, return that primitive type if it is a subtype
                    if (isTypeSubtypeOf(typeInfo.type, type)) {
                        return typeInfo.type;
                    }
                    // Otherwise, remove all types that aren't of the primitive type kind. This can happen when the type is
                    // union of enum types and other types.
                    return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ false, /*allowEmptyUnionResult*/ false);
                }
                else {
                    // Assumed result is false. If check was for a primitive type, remove that primitive type
                    if (typeInfo) {
                        return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ false);
                    }
                    // Otherwise we don't have enough information to do anything.
                    return type;
                }
            }
            function narrowTypeByAnd(type, expr, assumeTrue) {
                if (assumeTrue) {
                    // The assumed result is true, therefore we narrow assuming each operand to be true.
                    return narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true);
                }
                else {
                    // The assumed result is false. This means either the first operand was false, or the first operand was true
                    // and the second operand was false. We narrow with those assumptions and union the two resulting types.
                    return getUnionType([
                        narrowType(type, expr.left, /*assumeTrue*/ false),
                        narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false)
                    ]);
                }
            }
            function narrowTypeByOr(type, expr, assumeTrue) {
                if (assumeTrue) {
                    // The assumed result is true. This means either the first operand was true, or the first operand was false
                    // and the second operand was true. We narrow with those assumptions and union the two resulting types.
                    return getUnionType([
                        narrowType(type, expr.left, /*assumeTrue*/ true),
                        narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ true)
                    ]);
                }
                else {
                    // The assumed result is false, therefore we narrow assuming each operand to be false.
                    return narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false);
                }
            }
            function narrowTypeByInstanceof(type, expr, assumeTrue) {
                // Check that type is not any, assumed result is true, and we have variable symbol on the left
                if (isTypeAny(type) || !assumeTrue || expr.left.kind !== 69 /* Identifier */ || getResolvedSymbol(expr.left) !== symbol) {
                    return type;
                }
                // Check that right operand is a function type with a prototype property
                var rightType = checkExpression(expr.right);
                if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
                    return type;
                }
                var targetType;
                var prototypeProperty = getPropertyOfType(rightType, "prototype");
                if (prototypeProperty) {
                    // Target type is type of the prototype property
                    var prototypePropertyType = getTypeOfSymbol(prototypeProperty);
                    if (!isTypeAny(prototypePropertyType)) {
                        targetType = prototypePropertyType;
                    }
                }
                if (!targetType) {
                    // Target type is type of construct signature
                    var constructSignatures;
                    if (rightType.flags & 2048 /* Interface */) {
                        constructSignatures = resolveDeclaredMembers(rightType).declaredConstructSignatures;
                    }
                    else if (rightType.flags & 65536 /* Anonymous */) {
                        constructSignatures = getSignaturesOfType(rightType, 1 /* Construct */);
                    }
                    if (constructSignatures && constructSignatures.length) {
                        targetType = getUnionType(ts.map(constructSignatures, function (signature) { return getReturnTypeOfSignature(getErasedSignature(signature)); }));
                    }
                }
                if (targetType) {
                    return getNarrowedType(type, targetType);
                }
                return type;
            }
            function getNarrowedType(originalType, narrowedTypeCandidate) {
                // If the current type is a union type, remove all constituents that aren't assignable to target. If that produces
                // 0 candidates, fall back to the assignability check
                if (originalType.flags & 16384 /* Union */) {
                    var assignableConstituents = ts.filter(originalType.types, function (t) { return isTypeAssignableTo(t, narrowedTypeCandidate); });
                    if (assignableConstituents.length) {
                        return getUnionType(assignableConstituents);
                    }
                }
                if (isTypeAssignableTo(narrowedTypeCandidate, originalType)) {
                    // Narrow to the target type if it's assignable to the current type
                    return narrowedTypeCandidate;
                }
                return originalType;
            }
            function narrowTypeByTypePredicate(type, expr, assumeTrue) {
                if (type.flags & 1 /* Any */) {
                    return type;
                }
                var signature = getResolvedSignature(expr);
                if (signature.typePredicate &&
                    expr.arguments[signature.typePredicate.parameterIndex] &&
                    getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) {
                    if (!assumeTrue) {
                        if (type.flags & 16384 /* Union */) {
                            return getUnionType(ts.filter(type.types, function (t) { return !isTypeSubtypeOf(t, signature.typePredicate.type); }));
                        }
                        return type;
                    }
                    return getNarrowedType(type, signature.typePredicate.type);
                }
                return type;
            }
            // Narrow the given type based on the given expression having the assumed boolean value. The returned type
            // will be a subtype or the same type as the argument.
            function narrowType(type, expr, assumeTrue) {
                switch (expr.kind) {
                    case 168 /* CallExpression */:
                        return narrowTypeByTypePredicate(type, expr, assumeTrue);
                    case 172 /* ParenthesizedExpression */:
                        return narrowType(type, expr.expression, assumeTrue);
                    case 181 /* BinaryExpression */:
                        var operator = expr.operatorToken.kind;
                        if (operator === 32 /* EqualsEqualsEqualsToken */ || operator === 33 /* ExclamationEqualsEqualsToken */) {
                            return narrowTypeByEquality(type, expr, assumeTrue);
                        }
                        else if (operator === 51 /* AmpersandAmpersandToken */) {
                            return narrowTypeByAnd(type, expr, assumeTrue);
                        }
                        else if (operator === 52 /* BarBarToken */) {
                            return narrowTypeByOr(type, expr, assumeTrue);
                        }
                        else if (operator === 91 /* InstanceOfKeyword */) {
                            return narrowTypeByInstanceof(type, expr, assumeTrue);
                        }
                        break;
                    case 179 /* PrefixUnaryExpression */:
                        if (expr.operator === 49 /* ExclamationToken */) {
                            return narrowType(type, expr.operand, !assumeTrue);
                        }
                        break;
                }
                return type;
            }
        }
function getNarrowedTypeOfSymbol(symbol, node) {
            var type = getTypeOfSymbol(symbol);
            // Only narrow when symbol is variable of type any or an object, union, or type parameter type
            if (node && symbol.flags & 3 /* Variable */) {
                if (isTypeAny(type) || type.flags & (80896 /* ObjectType */ | 16384 /* Union */ | 512 /* TypeParameter */)) {
                    loop: while (node.parent) {
                        var child = node;
                        node = node.parent;
                        var narrowedType = type;
                        switch (node.kind) {
                            case 196 /* IfStatement */:
                                // In a branch of an if statement, narrow based on controlling expression
                                if (child !== node.expression) {
                                    narrowedType = narrowType(type, node.expression, /*assumeTrue*/ child === node.thenStatement);
                                }
                                break;
                            case 182 /* ConditionalExpression */:
                                // In a branch of a conditional expression, narrow based on controlling condition
                                if (child !== node.condition) {
                                    narrowedType = narrowType(type, node.condition, /*assumeTrue*/ child === node.whenTrue);
                                }
                                break;
                            case 181 /* BinaryExpression */:
                                // In the right operand of an && or ||, narrow based on left operand
                                if (child === node.right) {
                                    if (node.operatorToken.kind === 51 /* AmpersandAmpersandToken */) {
                                        narrowedType = narrowType(type, node.left, /*assumeTrue*/ true);
                                    }
                                    else if (node.operatorToken.kind === 52 /* BarBarToken */) {
                                        narrowedType = narrowType(type, node.left, /*assumeTrue*/ false);
                                    }
                                }
                                break;
                            case 248 /* SourceFile */:
                            case 218 /* ModuleDeclaration */:
                            case 213 /* FunctionDeclaration */:
                            case 143 /* MethodDeclaration */:
                            case 142 /* MethodSignature */:
                            case 145 /* GetAccessor */:
                            case 146 /* SetAccessor */:
                            case 144 /* Constructor */:
                                // Stop at the first containing function or module declaration
                                break loop;
                        }
                        // Use narrowed type if construct contains no assignments to variable
                        if (narrowedType !== type) {
                            if (isVariableAssignedWithin(symbol, node)) {
                                break;
                            }
                            type = narrowedType;
                        }
                    }
                }
            }
            return type;
            function narrowTypeByEquality(type, expr, assumeTrue) {
                // Check that we have 'typeof <symbol>' on the left and string literal on the right
                if (expr.left.kind !== 176 /* TypeOfExpression */ || expr.right.kind !== 9 /* StringLiteral */) {
                    return type;
                }
                var left = expr.left;
                var right = expr.right;
                if (left.expression.kind !== 69 /* Identifier */ || getResolvedSymbol(left.expression) !== symbol) {
                    return type;
                }
                var typeInfo = primitiveTypeInfo[right.text];
                if (expr.operatorToken.kind === 33 /* ExclamationEqualsEqualsToken */) {
                    assumeTrue = !assumeTrue;
                }
                if (assumeTrue) {
                    // Assumed result is true. If check was not for a primitive type, remove all primitive types
                    if (!typeInfo) {
                        return removeTypesFromUnionType(type, /*typeKind*/ 258 /* StringLike */ | 132 /* NumberLike */ | 8 /* Boolean */ | 16777216 /* ESSymbol */,
                            /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ false);
                    }
                    // Check was for a primitive type, return that primitive type if it is a subtype
                    if (isTypeSubtypeOf(typeInfo.type, type)) {
                        return typeInfo.type;
                    }
                    // Otherwise, remove all types that aren't of the primitive type kind. This can happen when the type is
                    // union of enum types and other types.
                    return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ false, /*allowEmptyUnionResult*/ false);
                }
                else {
                    // Assumed result is false. If check was for a primitive type, remove that primitive type
                    if (typeInfo) {
                        return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ true, /*allowEmptyUnionResult*/ false);
                    }
                    // Otherwise we don't have enough information to do anything.
                    return type;
                }
            }
            function narrowTypeByAnd(type, expr, assumeTrue) {
                if (assumeTrue) {
                    // The assumed result is true, therefore we narrow assuming each operand to be true.
                    return narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true);
                }
                else {
                    // The assumed result is false. This means either the first operand was false, or the first operand was true
                    // and the second operand was false. We narrow with those assumptions and union the two resulting types.
                    return getUnionType([
                        narrowType(type, expr.left, /*assumeTrue*/ false),
                        narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false)
                    ]);
                }
            }
            function narrowTypeByOr(type, expr, assumeTrue) {
                if (assumeTrue) {
                    // The assumed result is true. This means either the first operand was true, or the first operand was false
                    // and the second operand was true. We narrow with those assumptions and union the two resulting types.
                    return getUnionType([
                        narrowType(type, expr.left, /*assumeTrue*/ true),
                        narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ true)
                    ]);
                }
                else {
                    // The assumed result is false, therefore we narrow assuming each operand to be false.
                    return narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false);
                }
            }
            function narrowTypeByInstanceof(type, expr, assumeTrue) {
                // Check that type is not any, assumed result is true, and we have variable symbol on the left
                if (isTypeAny(type) || !assumeTrue || expr.left.kind !== 69 /* Identifier */ || getResolvedSymbol(expr.left) !== symbol) {
                    return type;
                }
                // Check that right operand is a function type with a prototype property
                var rightType = checkExpression(expr.right);
                if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
                    return type;
                }
                var targetType;
                var prototypeProperty = getPropertyOfType(rightType, "prototype");
                if (prototypeProperty) {
                    // Target type is type of the prototype property
                    var prototypePropertyType = getTypeOfSymbol(prototypeProperty);
                    if (!isTypeAny(prototypePropertyType)) {
                        targetType = prototypePropertyType;
                    }
                }
                if (!targetType) {
                    // Target type is type of construct signature
                    var constructSignatures;
                    if (rightType.flags & 2048 /* Interface */) {
                        constructSignatures = resolveDeclaredMembers(rightType).declaredConstructSignatures;
                    }
                    else if (rightType.flags & 65536 /* Anonymous */) {
                        constructSignatures = getSignaturesOfType(rightType, 1 /* Construct */);
                    }
                    if (constructSignatures && constructSignatures.length) {
                        targetType = getUnionType(ts.map(constructSignatures, function (signature) { return getReturnTypeOfSignature(getErasedSignature(signature)); }));
                    }
                }
                if (targetType) {
                    return getNarrowedType(type, targetType);
                }
                return type;
            }
            function getNarrowedType(originalType, narrowedTypeCandidate) {
                // If the current type is a union type, remove all constituents that aren't assignable to target. If that produces
                // 0 candidates, fall back to the assignability check
                if (originalType.flags & 16384 /* Union */) {
                    var assignableConstituents = ts.filter(originalType.types, function (t) { return isTypeAssignableTo(t, narrowedTypeCandidate); });
                    if (assignableConstituents.length) {
                        return getUnionType(assignableConstituents);
                    }
                }
                if (isTypeAssignableTo(narrowedTypeCandidate, originalType)) {
                    // Narrow to the target type if it's assignable to the current type
                    return narrowedTypeCandidate;
                }
                return originalType;
            }
            function narrowTypeByTypePredicate(type, expr, assumeTrue) {
                if (type.flags & 1 /* Any */) {
                    return type;
                }
                var signature = getResolvedSignature(expr);
                if (signature.typePredicate &&
                    expr.arguments[signature.typePredicate.parameterIndex] &&
                    getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) {
                    if (!assumeTrue) {
                        if (type.flags & 16384 /* Union */) {
                            return getUnionType(ts.filter(type.types, function (t) { return !isTypeSubtypeOf(t, signature.typePredicate.type); }));
                        }
                        return type;
                    }
                    return getNarrowedType(type, signature.typePredicate.type);
                }
                return type;
            }
            // Narrow the given type based on the given expression having the assumed boolean value. The returned type
            // will be a subtype or the same type as the argument.
            function narrowType(type, expr, assumeTrue) {
                switch (expr.kind) {
                    case 168 /* CallExpression */:
                        return narrowTypeByTypePredicate(type, expr, assumeTrue);
                    case 172 /* ParenthesizedExpression */:
                        return narrowType(type, expr.expression, assumeTrue);
                    case 181 /* BinaryExpression */:
                        var operator = expr.operatorToken.kind;
                        if (operator === 32 /* EqualsEqualsEqualsToken */ || operator === 33 /* ExclamationEqualsEqualsToken */) {
                            return narrowTypeByEquality(type, expr, assumeTrue);
                        }
                        else if (operator === 51 /* AmpersandAmpersandToken */) {
                            return narrowTypeByAnd(type, expr, assumeTrue);
                        }
                        else if (operator === 52 /* BarBarToken */) {
                            return narrowTypeByOr(type, expr, assumeTrue);
                        }
                        else if (operator === 91 /* InstanceOfKeyword */) {
                            return narrowTypeByInstanceof(type, expr, assumeTrue);
                        }
                        break;
                    case 179 /* PrefixUnaryExpression */:
                        if (expr.operator === 49 /* ExclamationToken */) {
                            return narrowType(type, expr.operand, !assumeTrue);
                        }
                        break;
                }
                return type;
            }
        }
Get the narrowed type of a given symbol at a given location