diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21633d97a3ec4..843904dd01826 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4590,8 +4590,8 @@ module ts { // Get the narrowed type of a given symbol at a given location function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) { var type = getTypeOfSymbol(symbol); - // Only narrow when symbol is variable of an object, union, or type parameter type - if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { + // Only narrow when symbol is variable of type any or an object, union, or type parameter type + if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) { loop: while (node.parent) { var child = node; node = node.parent; @@ -4647,21 +4647,16 @@ module ts { if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) { return type; } - var left = expr.left; var right = expr.right; - if (left.expression.kind !== SyntaxKind.Identifier || - getResolvedSymbol(left.expression) !== symbol) { - + if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(left.expression) !== symbol) { return type; } - var t = right.text; var checkType: Type = t === "string" ? stringType : t === "number" ? numberType : t === "boolean" ? booleanType : emptyObjectType; if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (assumeTrue) { // The assumed result is true. If check was for a primitive type, that type is the narrowed type. Otherwise we can // remove the primitive types from the narrowed type. @@ -4705,8 +4700,8 @@ module ts { } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // Check that assumed result is true and we have variable symbol on the left - if (!assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { + // Check that type is not any, assumed result is true, and we have variable symbol on the left + if (type.flags & TypeFlags.Any || !assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) { return type; } // Check that right operand is a function type with a prototype property diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 00cd54b94ebcb..b70c4a7a1d5cb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -12,43 +12,50 @@ module ts { return new (getNodeConstructor(kind))(); } - // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes - // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, - // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns - // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. - export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodes?: (nodes: Node[]) => T): T { - function child(node: Node): T { - if (node) { - return cbNode(node); - } + function visitNode(cbNode: (node: Node) => T, node: Node): T { + if (node) { + return cbNode(node); } - function children(nodes: Node[]) { - if (nodes) { - if (cbNodes) { - return cbNodes(nodes); - } + } - for (var i = 0, len = nodes.length; i < len; i++) { - var result = cbNode(nodes[i]) - if (result) { - return result; - } - } + function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { + if (nodes) { + return cbNodes(nodes); + } + } - return undefined; + function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { + if (nodes) { + for (var i = 0, len = nodes.length; i < len; i++) { + var result = cbNode(nodes[i]); + if (result) { + return result; + } } } + } + + // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes + // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, + // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns + // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. + export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { if (!node) { return; } + // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray + // callback parameters, but that causes a closure allocation for each invocation with noticeable effects + // on performance. + var visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode; + var cbNodes = cbNodeArray || cbNode; switch (node.kind) { case SyntaxKind.QualifiedName: - return child((node).left) || - child((node).right); + return visitNode(cbNode, (node).left) || + visitNode(cbNode, (node).right); case SyntaxKind.TypeParameter: - return child((node).name) || - child((node).constraint) || - child((node).expression); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).constraint) || + visitNode(cbNode, (node).expression); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: @@ -56,22 +63,22 @@ module ts { case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: - return children(node.modifiers) || - child((node).propertyName) || - child((node).dotDotDotToken) || - child((node).name) || - child((node).questionToken) || - child((node).type) || - child((node).initializer); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).propertyName) || + visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - return children(node.modifiers) || - children((node).typeParameters) || - children((node).parameters) || - child((node).type); + return visitNodes(cbNodes, node.modifiers) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).parameters) || + visitNode(cbNode, (node).type); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: @@ -80,182 +87,182 @@ module ts { case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: - return children(node.modifiers) || - child((node).asteriskToken) || - child((node).name) || - child((node).questionToken) || - children((node).typeParameters) || - children((node).parameters) || - child((node).type) || - child((node).body); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).asteriskToken) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).questionToken) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).parameters) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).body); case SyntaxKind.TypeReference: - return child((node).typeName) || - children((node).typeArguments); + return visitNode(cbNode, (node).typeName) || + visitNodes(cbNodes, (node).typeArguments); case SyntaxKind.TypeQuery: - return child((node).exprName); + return visitNode(cbNode, (node).exprName); case SyntaxKind.TypeLiteral: - return children((node).members); + return visitNodes(cbNodes, (node).members); case SyntaxKind.ArrayType: - return child((node).elementType); + return visitNode(cbNode, (node).elementType); case SyntaxKind.TupleType: - return children((node).elementTypes); + return visitNodes(cbNodes, (node).elementTypes); case SyntaxKind.UnionType: - return children((node).types); + return visitNodes(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: - return child((node).type); + return visitNode(cbNode, (node).type); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - return children((node).elements); + return visitNodes(cbNodes, (node).elements); case SyntaxKind.ArrayLiteralExpression: - return children((node).elements); + return visitNodes(cbNodes, (node).elements); case SyntaxKind.ObjectLiteralExpression: - return children((node).properties); + return visitNodes(cbNodes, (node).properties); case SyntaxKind.PropertyAccessExpression: - return child((node).expression) || - child((node).name); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).name); case SyntaxKind.ElementAccessExpression: - return child((node).expression) || - child((node).argumentExpression); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).argumentExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: - return child((node).expression) || - children((node).typeArguments) || - children((node).arguments); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).typeArguments) || + visitNodes(cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: - return child((node).tag) || - child((node).template); + return visitNode(cbNode, (node).tag) || + visitNode(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: - return child((node).type) || - child((node).expression); + return visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).expression); case SyntaxKind.ParenthesizedExpression: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.DeleteExpression: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.TypeOfExpression: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.VoidExpression: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.PrefixUnaryExpression: - return child((node).operand); + return visitNode(cbNode, (node).operand); case SyntaxKind.YieldExpression: - return child((node).asteriskToken) || - child((node).expression); + return visitNode(cbNode, (node).asteriskToken) || + visitNode(cbNode, (node).expression); case SyntaxKind.PostfixUnaryExpression: - return child((node).operand); + return visitNode(cbNode, (node).operand); case SyntaxKind.BinaryExpression: - return child((node).left) || - child((node).right); + return visitNode(cbNode, (node).left) || + visitNode(cbNode, (node).right); case SyntaxKind.ConditionalExpression: - return child((node).condition) || - child((node).whenTrue) || - child((node).whenFalse); + return visitNode(cbNode, (node).condition) || + visitNode(cbNode, (node).whenTrue) || + visitNode(cbNode, (node).whenFalse); case SyntaxKind.SpreadElementExpression: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: - return children((node).statements); + return visitNodes(cbNodes, (node).statements); case SyntaxKind.SourceFile: - return children((node).statements) || - child((node).endOfFileToken); + return visitNodes(cbNodes, (node).statements) || + visitNode(cbNode, (node).endOfFileToken); case SyntaxKind.VariableStatement: - return children(node.modifiers) || - child((node).declarationList); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).declarationList); case SyntaxKind.VariableDeclarationList: - return children((node).declarations); + return visitNodes(cbNodes, (node).declarations); case SyntaxKind.ExpressionStatement: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.IfStatement: - return child((node).expression) || - child((node).thenStatement) || - child((node).elseStatement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).thenStatement) || + visitNode(cbNode, (node).elseStatement); case SyntaxKind.DoStatement: - return child((node).statement) || - child((node).expression); + return visitNode(cbNode, (node).statement) || + visitNode(cbNode, (node).expression); case SyntaxKind.WhileStatement: - return child((node).expression) || - child((node).statement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.ForStatement: - return child((node).initializer) || - child((node).condition) || - child((node).iterator) || - child((node).statement); + return visitNode(cbNode, (node).initializer) || + visitNode(cbNode, (node).condition) || + visitNode(cbNode, (node).iterator) || + visitNode(cbNode, (node).statement); case SyntaxKind.ForInStatement: - return child((node).initializer) || - child((node).expression) || - child((node).statement); + return visitNode(cbNode, (node).initializer) || + visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: - return child((node).label); + return visitNode(cbNode, (node).label); case SyntaxKind.ReturnStatement: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.WithStatement: - return child((node).expression) || - child((node).statement); + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).statement); case SyntaxKind.SwitchStatement: - return child((node).expression) || - children((node).clauses); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).clauses); case SyntaxKind.CaseClause: - return child((node).expression) || - children((node).statements); + return visitNode(cbNode, (node).expression) || + visitNodes(cbNodes, (node).statements); case SyntaxKind.DefaultClause: - return children((node).statements); + return visitNodes(cbNodes, (node).statements); case SyntaxKind.LabeledStatement: - return child((node).label) || - child((node).statement); + return visitNode(cbNode, (node).label) || + visitNode(cbNode, (node).statement); case SyntaxKind.ThrowStatement: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.TryStatement: - return child((node).tryBlock) || - child((node).catchClause) || - child((node).finallyBlock); + return visitNode(cbNode, (node).tryBlock) || + visitNode(cbNode, (node).catchClause) || + visitNode(cbNode, (node).finallyBlock); case SyntaxKind.CatchClause: - return child((node).name) || - child((node).type) || - child((node).block); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).block); case SyntaxKind.ClassDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).typeParameters) || - children((node).heritageClauses) || - children((node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).heritageClauses) || + visitNodes(cbNodes, (node).members); case SyntaxKind.InterfaceDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).typeParameters) || - children((node).heritageClauses) || - children((node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).typeParameters) || + visitNodes(cbNodes, (node).heritageClauses) || + visitNodes(cbNodes, (node).members); case SyntaxKind.TypeAliasDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).type); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).type); case SyntaxKind.EnumDeclaration: - return children(node.modifiers) || - child((node).name) || - children((node).members); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNodes(cbNodes, (node).members); case SyntaxKind.EnumMember: - return child((node).name) || - child((node).initializer); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).initializer); case SyntaxKind.ModuleDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).body); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).body); case SyntaxKind.ImportDeclaration: - return children(node.modifiers) || - child((node).name) || - child((node).moduleReference); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).moduleReference); case SyntaxKind.ExportAssignment: - return children(node.modifiers) || - child((node).exportName); + return visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (node).exportName); case SyntaxKind.TemplateExpression: - return child((node).head) || children((node).templateSpans); + return visitNode(cbNode, (node).head) || visitNodes(cbNodes, (node).templateSpans); case SyntaxKind.TemplateSpan: - return child((node).expression) || child((node).literal); + return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).literal); case SyntaxKind.ComputedPropertyName: - return child((node).expression); + return visitNode(cbNode, (node).expression); case SyntaxKind.HeritageClause: - return children((node).types); + return visitNodes(cbNodes, (node).types); case SyntaxKind.ExternalModuleReference: - return child((node).expression); + return visitNode(cbNode, (node).expression); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 60ed8352b9a3d..9ebab8dc2207c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1331,8 +1331,6 @@ module ts { // Generic class and interface types export interface GenericType extends InterfaceType, TypeReference { instantiations: Map; // Generic instantiation cache - openReferenceTargets: GenericType[]; // Open type reference targets - openReferenceChecks: Map; // Open type reference check cache } export interface TupleType extends ObjectType { diff --git a/tests/baselines/reference/typeGuardsWithAny.errors.txt b/tests/baselines/reference/typeGuardsWithAny.errors.txt new file mode 100644 index 0000000000000..653c89c7554ad --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithAny.errors.txt @@ -0,0 +1,49 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(11,7): error TS2339: Property 'p' does not exist on type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(18,7): error TS2339: Property 'p' does not exist on type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts(25,7): error TS2339: Property 'p' does not exist on type 'boolean'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts (3 errors) ==== + var x: any = { p: 0 }; + + if (x instanceof Object) { + x.p; // No error, type any unaffected by instanceof type guard + } + else { + x.p; // No error, type any unaffected by instanceof type guard + } + + if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'string'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'number'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check + ~ +!!! error TS2339: Property 'p' does not exist on type 'boolean'. + } + else { + x.p; // No error, type unaffected in this branch + } + + if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check + } + else { + x.p; // No error, type unaffected in this branch + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithAny.js b/tests/baselines/reference/typeGuardsWithAny.js index 56676fb6d6884..be3d64b806fc1 100644 --- a/tests/baselines/reference/typeGuardsWithAny.js +++ b/tests/baselines/reference/typeGuardsWithAny.js @@ -1,18 +1,71 @@ //// [typeGuardsWithAny.ts] var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch } //// [typeGuardsWithAny.js] var x = { p: 0 }; if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} +else { + x.p; // No error, type any unaffected by instanceof type guard +} +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type unaffected in this branch } diff --git a/tests/baselines/reference/typeGuardsWithAny.types b/tests/baselines/reference/typeGuardsWithAny.types deleted file mode 100644 index ee628c9c2497d..0000000000000 --- a/tests/baselines/reference/typeGuardsWithAny.types +++ /dev/null @@ -1,23 +0,0 @@ -=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts === -var x: any = { p: 0 }; ->x : any ->{ p: 0 } : { p: number; } ->p : number - -if (x instanceof Object) { ->x instanceof Object : boolean ->x : any ->Object : ObjectConstructor - - x.p; // No error, type any unaffected by type guard ->x.p : any ->x : any ->p : any -} -else { - x.p; // No error, type any unaffected by type guard ->x.p : any ->x : any ->p : any -} - diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts index e7e756ea8e041..a6379bf95b082 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithAny.ts @@ -1,7 +1,36 @@ var x: any = { p: 0 }; + if (x instanceof Object) { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard } else { - x.p; // No error, type any unaffected by type guard + x.p; // No error, type any unaffected by instanceof type guard +} + +if (typeof x === "string") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "number") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "boolean") { + x.p; // Error, type any narrowed by primitive type check +} +else { + x.p; // No error, type unaffected in this branch +} + +if (typeof x === "object") { + x.p; // No error, type any only affected by primitive type check +} +else { + x.p; // No error, type unaffected in this branch }