@@ -3844,44 +3844,285 @@ module ts {
3844
3844
3845
3845
// EXPRESSION TYPE CHECKING
3846
3846
3847
- function checkIdentifier ( node : Identifier ) : Type {
3848
- function isInTypeQuery ( node : Node ) : boolean {
3849
- // TypeScript 1.0 spec (April 2014): 3.6.3
3850
- // A type query consists of the keyword typeof followed by an expression.
3851
- // The expression is restricted to a single identifier or a sequence of identifiers separated by periods
3852
- while ( node ) {
3847
+ function getResolvedSymbol ( node : Identifier ) : Symbol {
3848
+ var links = getNodeLinks ( node ) ;
3849
+ if ( ! links . resolvedSymbol ) {
3850
+ links . resolvedSymbol = resolveName ( node , node . text , SymbolFlags . Value | SymbolFlags . ExportValue , Diagnostics . Cannot_find_name_0 , identifierToString ( node ) ) || unknownSymbol ;
3851
+ }
3852
+ return links . resolvedSymbol ;
3853
+ }
3854
+
3855
+ function isInTypeQuery ( node : Node ) : boolean {
3856
+ // TypeScript 1.0 spec (April 2014): 3.6.3
3857
+ // A type query consists of the keyword typeof followed by an expression.
3858
+ // The expression is restricted to a single identifier or a sequence of identifiers separated by periods
3859
+ while ( node ) {
3860
+ switch ( node . kind ) {
3861
+ case SyntaxKind . TypeQuery :
3862
+ return true ;
3863
+ case SyntaxKind . Identifier :
3864
+ case SyntaxKind . QualifiedName :
3865
+ node = node . parent ;
3866
+ continue ;
3867
+ default :
3868
+ return false ;
3869
+ }
3870
+ }
3871
+ Debug . fail ( "should not get here" ) ;
3872
+ }
3873
+
3874
+ // Remove one or more primitive types from a union type
3875
+ function subtractPrimitiveTypes ( type : Type , subtractMask : TypeFlags ) : Type {
3876
+ if ( type . flags & TypeFlags . Union ) {
3877
+ var types = ( < UnionType > type ) . types ;
3878
+ if ( forEach ( types , t => t . flags & subtractMask ) ) {
3879
+ var newTypes : Type [ ] = [ ] ;
3880
+ forEach ( types , t => {
3881
+ if ( ! ( t . flags & subtractMask ) ) {
3882
+ newTypes . push ( t ) ;
3883
+ }
3884
+ } ) ;
Has conversations. Original line has conversations.
3885
+ return getUnionType ( newTypes ) ;
3886
+ }
3887
+ }
3888
+ return type ;
3889
+ }
3890
+
3891
+ // Check if a given variable is assigned within a given syntax node
3892
+ function IsVariableAssignedWithin ( symbol : Symbol , node : Node ) : boolean {
Has a conversation. Original line has a conversation.
3893
+ var links = getNodeLinks ( node ) ;
3894
+ if ( links . assignmentChecks ) {
3895
+ var cachedResult = links . assignmentChecks [ symbol . id ] ;
3896
+ if ( cachedResult !== undefined ) {
3897
+ return cachedResult ;
3898
+ }
3899
+ }
3900
+ else {
3901
+ links . assignmentChecks = { } ;
3902
+ }
3903
+ return links . assignmentChecks [ symbol . id ] = isAssignedIn ( node ) ;
3904
+
3905
+ function isAssignedInBinaryExpression ( node : BinaryExpression ) {
3906
+ if ( node . operator >= SyntaxKind . FirstAssignment && node . operator <= SyntaxKind . LastAssignment ) {
3907
+ var n = node . left ;
3908
+ while ( n . kind === SyntaxKind . ParenExpression ) {
3909
+ n = ( < ParenExpression > n ) . expression ;
3910
+ }
3911
+ if ( n . kind === SyntaxKind . Identifier && getResolvedSymbol ( < Identifier > n ) === symbol ) {
3912
+ return true ;
3913
+ }
3914
+ }
3915
+ return forEachChild ( node , isAssignedIn ) ;
3916
+ }
3917
+
3918
+ function isAssignedInVariableDeclaration ( node : VariableDeclaration ) {
3919
+ if ( getSymbolOfNode ( node ) === symbol && node . initializer ) {
3920
+ return true ;
3921
+ }
3922
+ return forEachChild ( node , isAssignedIn ) ;
3923
+ }
3924
+
3925
+ function isAssignedIn ( node : Node ) : boolean {
3926
+ switch ( node . kind ) {
3927
+ case SyntaxKind . BinaryExpression :
3928
+ return isAssignedInBinaryExpression ( < BinaryExpression > node ) ;
3929
+ case SyntaxKind . VariableDeclaration :
3930
+ return isAssignedInVariableDeclaration ( < VariableDeclaration > node ) ;
3931
+ case SyntaxKind . ArrayLiteral :
3932
+ case SyntaxKind . ObjectLiteral :
3933
+ case SyntaxKind . PropertyAccess :
3934
+ case SyntaxKind . IndexedAccess :
3935
+ case SyntaxKind . CallExpression :
3936
+ case SyntaxKind . NewExpression :
3937
+ case SyntaxKind . TypeAssertion :
3938
+ case SyntaxKind . ParenExpression :
3939
+ case SyntaxKind . PrefixOperator :
3940
+ case SyntaxKind . PostfixOperator :
3941
+ case SyntaxKind . ConditionalExpression :
3942
+ case SyntaxKind . Block :
3943
+ case SyntaxKind . VariableStatement :
3944
+ case SyntaxKind . ExpressionStatement :
3945
+ case SyntaxKind . IfStatement :
3946
+ case SyntaxKind . DoStatement :
3947
+ case SyntaxKind . WhileStatement :
3948
+ case SyntaxKind . ForStatement :
3949
+ case SyntaxKind . ForInStatement :
3950
+ case SyntaxKind . ReturnStatement :
3951
+ case SyntaxKind . WithStatement :
3952
+ case SyntaxKind . SwitchStatement :
3953
+ case SyntaxKind . CaseClause :
3954
+ case SyntaxKind . DefaultClause :
3955
+ case SyntaxKind . LabeledStatement :
3956
+ case SyntaxKind . ThrowStatement :
3957
+ case SyntaxKind . TryStatement :
3958
+ case SyntaxKind . TryBlock :
3959
+ case SyntaxKind . CatchBlock :
3960
+ case SyntaxKind . FinallyBlock :
Has conversations. Original line has conversations.
3961
+ return forEachChild ( node , isAssignedIn ) ;
3962
+ }
3963
+ return false ;
3964
+ }
3965
+ }
3966
+
3967
+ // Get the narrowed type of a given symbol at a given location
3968
+ function getNarrowedTypeOfSymbol ( symbol : Symbol , node : Node ) {
Has conversations. Original line has conversations.
3969
+ var type = getTypeOfSymbol ( symbol ) ;
3970
+ // Only narrow when symbol is variable of a non-primitive type
3971
+ if ( symbol . flags & SymbolFlags . Variable && isTypeAnyOrObjectOrTypeParameter ( type ) ) {
3972
+ while ( true ) {
3973
+ var child = node ;
3974
+ node = node . parent ;
3975
+ // Stop at containing function or module block
3976
+ if ( ! node || node . kind === SyntaxKind . FunctionBlock || node . kind === SyntaxKind . ModuleBlock ) {
Has conversations. Original line has conversations.
3977
+ break ;
3978
+ }
3979
+ var narrowedType = type ;
3853
3980
switch ( node . kind ) {
3854
- case SyntaxKind . TypeQuery :
3855
- return true ;
3856
- case SyntaxKind . Identifier :
3857
- case SyntaxKind . QualifiedName :
3858
- node = node . parent ;
3859
- continue ;
3860
- default :
3861
- return false ;
3981
+ case SyntaxKind . IfStatement :
3982
+ // In a branch of an if statement, narrow based on controlling expression
3983
+ if ( child !== ( < IfStatement > node ) . expression ) {
3984
+ narrowedType = narrowType ( type , ( < IfStatement > node ) . expression , child === ( < IfStatement > node ) . thenStatement ) ;
Has a conversation. Original line has a conversation.
3985
+ }
3986
+ break ;
3987
+ case SyntaxKind . ConditionalExpression :
3988
+ // In a branch of a conditional expression, narrow based on controlling condition
3989
+ if ( child !== ( < ConditionalExpression > node ) . condition ) {
3990
+ narrowedType = narrowType ( type , ( < ConditionalExpression > node ) . condition , child === ( < ConditionalExpression > node ) . whenTrue ) ;
3991
+ }
3992
+ break ;
3993
+ case SyntaxKind . BinaryExpression :
3994
+ // In the right operand of an && or ||, narrow based on left operand
3995
+ if ( child === ( < BinaryExpression > node ) . right ) {
3996
+ if ( ( < BinaryExpression > node ) . operator === SyntaxKind . AmpersandAmpersandToken ) {
3997
+ narrowedType = narrowType ( type , ( < BinaryExpression > node ) . left , true ) ;
3998
+ }
3999
+ else if ( ( < BinaryExpression > node ) . operator === SyntaxKind . BarBarToken ) {
4000
+ narrowedType = narrowType ( type , ( < BinaryExpression > node ) . left , false ) ;
4001
+ }
4002
+ }
4003
+ break ;
4004
+ }
4005
+ // Only use narrowed type if construct contains no assignments to variable
4006
+ if ( narrowedType !== type && ! IsVariableAssignedWithin ( symbol , node ) ) {
Has conversations. Original line has conversations.
4007
+ type = narrowedType ;
3862
4008
}
3863
4009
}
3864
- Debug . fail ( "should not get here" ) ;
4010
+ }
4011
+ return type ;
4012
+
4013
+ function narrowTypeByEquality ( type : Type , expr : BinaryExpression , assumeTrue : boolean ) : Type {
4014
+ var left = < UnaryExpression > expr . left ;
4015
+ var right = < LiteralExpression > expr . right ;
4016
+ // Check that we have 'typeof <symbol>' on the left and string literal on the right
Has conversations. Original line has conversations.
4017
+ if ( left . kind !== SyntaxKind . PrefixOperator || left . operator !== SyntaxKind . TypeOfKeyword ||
4018
+ left . operand . kind !== SyntaxKind . Identifier || right . kind !== SyntaxKind . StringLiteral ||
4019
+ getResolvedSymbol ( < Identifier > left . operand ) !== symbol ) {
4020
+ return type ;
4021
+ }
4022
+ var t = right . text ;
4023
+ var checkType : Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType ;
4024
+ if ( expr . operator === SyntaxKind . ExclamationEqualsEqualsToken ) {
4025
+ assumeTrue = ! assumeTrue ;
4026
+ }
4027
+ if ( assumeTrue ) {
4028
+ // The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can
4029
+ // remove the primitive types from the narrowed type.
4030
+ return checkType === emptyObjectType ? subtractPrimitiveTypes ( type , TypeFlags . String | TypeFlags . Number | TypeFlags . Boolean ) : checkType ;
4031
+ }
4032
+ else {
4033
+ // The assumed result is false. If check was for a primitive type we can remove that type from the narrowed type.
4034
+ // Otherwise we don't have enough information to do anything.
4035
+ return checkType === emptyObjectType ? type : subtractPrimitiveTypes ( type , checkType . flags ) ;
4036
+ }
3865
4037
}
3866
4038
3867
- var symbol = resolveName ( node , node . text , SymbolFlags . Value | SymbolFlags . ExportValue , Diagnostics . Cannot_find_name_0 , identifierToString ( node ) ) ;
3868
- if ( ! symbol ) {
3869
- symbol = unknownSymbol ;
4039
+ function narrowTypeByAnd ( type : Type , expr : BinaryExpression , assumeTrue : boolean ) : Type {
4040
+ if ( assumeTrue ) {
4041
+ // The assumed result is true, therefore we narrow assuming each operand to be true.
4042
+ return narrowType ( narrowType ( type , expr . left , true ) , expr . right , true ) ;
4043
+ }
4044
+ else {
4045
+ // The assumed result is true. This means either the first operand was false, or the first operand was true
Has conversations. Original line has conversations.
4046
+ // and the second operand was false. We narrow with those assumptions and union the two resulting types.
4047
+ return getUnionType ( [ narrowType ( type , expr . left , false ) , narrowType ( narrowType ( type , expr . left , true ) , expr . right , false ) ] ) ;
4048
+ }
3870
4049
}
3871
4050
4051
+ function narrowTypeByOr ( type : Type , expr : BinaryExpression , assumeTrue : boolean ) : Type {
4052
+ if ( assumeTrue ) {
4053
+ // The assumed result is true. This means either the first operand was true, or the first operand was false
4054
+ // and the second operand was true. We narrow with those assumptions and union the two resulting types.
4055
+ return getUnionType ( [ narrowType ( type , expr . left , true ) , narrowType ( narrowType ( type , expr . left , false ) , expr . right , true ) ] ) ;
4056
+ }
4057
+ else {
4058
+ // The assumed result is false, therefore we narrow assuming each operand to be false.
4059
+ return narrowType ( narrowType ( type , expr . left , false ) , expr . right , false ) ;
4060
+ }
4061
+ }
4062
+
4063
+ function narrowTypeByInstanceof ( type : Type , expr : BinaryExpression , assumeTrue : boolean ) : Type {
Has conversations. Original line has conversations.
4064
+ // Check that we have variable symbol on the left
4065
+ if ( expr . left . kind !== SyntaxKind . Identifier || getResolvedSymbol ( < Identifier > expr . left ) !== symbol ) {
4066
+ return type ;
4067
+ }
4068
+ // Check that right operand is a function type with a prototype property
4069
+ var rightType = checkExpression ( expr . right ) ;
4070
+ if ( ! isTypeSubtypeOf ( rightType , globalFunctionType ) ) {
4071
+ return type ;
4072
+ }
4073
+ var prototypeProperty = getPropertyOfType ( getApparentType ( rightType ) , "prototype" ) ;
Has conversations. Original line has conversations.
4074
+ if ( ! prototypeProperty ) {
4075
+ return type ;
4076
+ }
4077
+ var prototypeType = getTypeOfSymbol ( prototypeProperty ) ;
4078
+ // Narrow to type of prototype property if it is a subtype of current type
4079
+ return isTypeSubtypeOf ( prototypeType , type ) ? prototypeType : type ;
Has conversations. Original line has conversations.
4080
+ }
4081
+
4082
+ // Narrow the given type based on the given expression having the assumed boolean value
4083
+ function narrowType ( type : Type , expr : Expression , assumeTrue : boolean ) : Type {
4084
+ switch ( expr . kind ) {
4085
+ case SyntaxKind . ParenExpression :
4086
+ return narrowType ( type , ( < ParenExpression > expr ) . expression , assumeTrue ) ;
4087
+ case SyntaxKind . BinaryExpression :
4088
+ var operator = ( < BinaryExpression > expr ) . operator ;
4089
+ if ( operator === SyntaxKind . EqualsEqualsEqualsToken || operator === SyntaxKind . ExclamationEqualsEqualsToken ) {
4090
+ return narrowTypeByEquality ( type , < BinaryExpression > expr , assumeTrue ) ;
4091
+ }
4092
+ else if ( operator === SyntaxKind . AmpersandAmpersandToken ) {
4093
+ return narrowTypeByAnd ( type , < BinaryExpression > expr , assumeTrue ) ;
4094
+ }
4095
+ else if ( operator === SyntaxKind . BarBarToken ) {
4096
+ return narrowTypeByOr ( type , < BinaryExpression > expr , assumeTrue ) ;
4097
+ }
4098
+ else if ( operator === SyntaxKind . InstanceOfKeyword ) {
4099
+ return narrowTypeByInstanceof ( type , < BinaryExpression > expr , assumeTrue ) ;
4100
+ }
4101
+ break ;
4102
+ case SyntaxKind . PrefixOperator :
4103
+ if ( ( < UnaryExpression > expr ) . operator === SyntaxKind . ExclamationToken ) {
4104
+ return narrowType ( type , ( < UnaryExpression > expr ) . operand , ! assumeTrue ) ;
4105
+ }
4106
+ break ;
4107
+ }
4108
+ return type ;
4109
+ }
4110
+ }
4111
+
4112
+ function checkIdentifier ( node : Identifier ) : Type {
4113
+ var symbol = getResolvedSymbol ( node ) ;
4114
+
3872
4115
if ( symbol . flags & SymbolFlags . Import ) {
3873
4116
// Mark the import as referenced so that we emit it in the final .js file.
3874
4117
// exception: identifiers that appear in type queries
3875
4118
getSymbolLinks ( symbol ) . referenced = ! isInTypeQuery ( node ) ;
3876
4119
}
3877
4120
3878
- getNodeLinks ( node ) . resolvedSymbol = symbol ;
3879
-
3880
4121
checkCollisionWithCapturedSuperVariable ( node , node ) ;
3881
4122
checkCollisionWithCapturedThisVariable ( node , node ) ;
3882
4123
checkCollisionWithIndexVariableInGeneratedCode ( node , node ) ;
3883
4124
3884
- return getTypeOfSymbol ( getExportSymbolOfValueSymbolIfExported ( symbol ) ) ;
4125
+ return getNarrowedTypeOfSymbol ( getExportSymbolOfValueSymbolIfExported ( symbol ) , node ) ;
3885
4126
}
3886
4127
3887
4128
function captureLexicalThis ( node : Node , container : Node ) : void {
@@ -5134,8 +5375,8 @@ module ts {
5134
5375
return numberType ;
5135
5376
}
5136
5377
5137
- function isTypeAnyTypeObjectTypeOrTypeParameter ( type : Type ) : boolean {
5138
- return type === anyType || ( ( type . flags & ( TypeFlags . ObjectType | TypeFlags . TypeParameter ) ) !== 0 ) ;
5378
+ function isTypeAnyOrObjectOrTypeParameter ( type : Type ) : boolean {
5379
+ return ( type . flags & ( TypeFlags . Any | TypeFlags . ObjectType | TypeFlags . TypeParameter ) ) !== 0 ;
5139
5380
}
5140
5381
5141
5382
function checkInstanceOfExpression ( node : BinaryExpression , leftType : Type , rightType : Type ) : Type {
@@ -5144,7 +5385,7 @@ module ts {
5144
5385
// and the right operand to be of type Any or a subtype of the 'Function' interface type.
5145
5386
// The result is always of the Boolean primitive type.
5146
5387
// NOTE: do not raise error if leftType is unknown as related error was already reported
5147
- if ( leftType !== unknownType && ! isTypeAnyTypeObjectTypeOrTypeParameter ( leftType ) ) {
5388
+ if ( leftType !== unknownType && ! isTypeAnyOrObjectOrTypeParameter ( leftType ) ) {
5148
5389
error ( node . left , Diagnostics . The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter ) ;
5149
5390
}
5150
5391
// NOTE: do not raise error if right is unknown as related error was already reported
@@ -5162,7 +5403,7 @@ module ts {
5162
5403
if ( leftType !== anyType && leftType !== stringType && leftType !== numberType ) {
5163
5404
error ( node . left , Diagnostics . The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number ) ;
5164
5405
}
5165
- if ( ! isTypeAnyTypeObjectTypeOrTypeParameter ( rightType ) ) {
5406
+ if ( ! isTypeAnyOrObjectOrTypeParameter ( rightType ) ) {
5166
5407
error ( node . right , Diagnostics . The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter ) ;
5167
5408
}
5168
5409
return booleanType ;
@@ -6338,7 +6579,7 @@ module ts {
6338
6579
var exprType = checkExpression ( node . expression ) ;
6339
6580
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
6340
6581
// in this case error about missing name is already reported - do not report extra one
6341
- if ( ! isTypeAnyTypeObjectTypeOrTypeParameter ( exprType ) && exprType !== unknownType ) {
6582
+ if ( ! isTypeAnyOrObjectOrTypeParameter ( exprType ) && exprType !== unknownType ) {
6342
6583
error ( node . expression , Diagnostics . The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter ) ;
6343
6584
}
6344
6585