From 02ccd911590e7e0f67dbaa54e76d8cff72b2d56f Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Thu, 23 Feb 2017 15:20:08 -0800 Subject: [PATCH 1/3] Infer class properties from methods and not just constructors --- src/compiler/binder.ts | 42 ++-- src/compiler/checker.ts | 48 ++-- .../inferringClassMembersFromAssignments.js | 150 +++++++++++ ...ferringClassMembersFromAssignments.symbols | 198 +++++++++++++++ ...inferringClassMembersFromAssignments.types | 234 ++++++++++++++++++ .../inferringClassMembersFromAssignments.ts | 79 ++++++ 6 files changed, 718 insertions(+), 33 deletions(-) create mode 100644 tests/baselines/reference/inferringClassMembersFromAssignments.js create mode 100644 tests/baselines/reference/inferringClassMembersFromAssignments.symbols create mode 100644 tests/baselines/reference/inferringClassMembersFromAssignments.types create mode 100644 tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 51830c50bf..2fc7317884 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2238,23 +2238,31 @@ namespace ts { function bindThisPropertyAssignment(node: BinaryExpression) { Debug.assert(isInJavaScriptFile(node)); - // Declare a 'member' if the container is an ES5 class or ES6 constructor - if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) { - container.symbol.members = container.symbol.members || createMap(); - // It's acceptable for multiple 'this' assignments of the same identifier to occur - declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); - } - else if (container.kind === SyntaxKind.Constructor) { - // this.foo assignment in a JavaScript class - // Bind this property to the containing class - const saveContainer = container; - container = container.parent; - const symbol = bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); - if (symbol) { - // constructor-declared symbols can be overwritten by subsequent method declarations - (symbol as Symbol).isReplaceableByMethod = true; - } - container = saveContainer; + switch (container.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + // Declare a 'member' if the container is an ES5 class or ES6 constructor + container.symbol.members = container.symbol.members || createMap(); + // It's acceptable for multiple 'this' assignments of the same identifier to occur + declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); + break; + + case SyntaxKind.Constructor: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + // this.foo assignment in a JavaScript class + // Bind this property to the containing class + const containingClass = container.parent; + const symbol = hasModifier(container, ModifierFlags.Static) + ? declareSymbol(containingClass.symbol.exports, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None) + : declareSymbol(containingClass.symbol.members, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None); + + if (symbol) { + // symbols declared through 'this' property assignements can be overwritten by subsequent method declarations + (symbol as Symbol).isReplaceableByMethod = true; + } + break; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 83fea11c99..93973fe71a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3472,25 +3472,41 @@ namespace ts { return undefined; } - // Return the inferred type for a variable, parameter, or property declaration - function getTypeForJSSpecialPropertyDeclaration(declaration: Declaration): Type { - const expression = declaration.kind === SyntaxKind.BinaryExpression ? declaration : - declaration.kind === SyntaxKind.PropertyAccessExpression ? getAncestor(declaration, SyntaxKind.BinaryExpression) : - undefined; + function getWidendedTypeFromJSSpecialPropetyDeclarations(symbol: Symbol) { + const types: Type[] = []; + let definedInConstructor = false; + let definedInMethod = false; + for (const declaration of symbol.declarations) { + const expression = declaration.kind === SyntaxKind.BinaryExpression ? declaration : + declaration.kind === SyntaxKind.PropertyAccessExpression ? getAncestor(declaration, SyntaxKind.BinaryExpression) : + undefined; - if (!expression) { - return unknownType; - } - - if (expression.flags & NodeFlags.JavaScriptFile) { - // If there is a JSDoc type, use it - const type = getTypeForDeclarationFromJSDocComment(expression.parent); - if (type && type !== unknownType) { - return getWidenedType(type); + if (!expression) { + return unknownType; } + + if (isPropertyAccessExpression(expression.left) && expression.left.expression.kind === SyntaxKind.ThisKeyword) { + if (getThisContainer(expression, /*includeArrowFunctions*/ false).kind === SyntaxKind.Constructor) { + definedInConstructor = true; + } + else { + definedInMethod = true; + } + } + + if (expression.flags & NodeFlags.JavaScriptFile) { + // If there is a JSDoc type, use it + const type = getTypeForDeclarationFromJSDocComment(expression.parent); + if (type && type !== unknownType) { + types.push(getWidenedType(type)); + continue; + } + } + + types.push(getWidenedLiteralType(checkExpressionCached(expression.right))); } - return getWidenedLiteralType(checkExpressionCached(expression.right)); + return getWidenedType(addOptionality(getUnionType(types, /*subtypeReduction*/ true), definedInMethod && !definedInConstructor)); } // Return the type implied by a binding pattern element. This is the type of the initializer of the element if @@ -3647,7 +3663,7 @@ namespace ts { // * className.prototype.method = expr if (declaration.kind === SyntaxKind.BinaryExpression || declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { - type = getWidenedType(getUnionType(map(symbol.declarations, getTypeForJSSpecialPropertyDeclaration), /*subtypeReduction*/ true)); + type = getWidendedTypeFromJSSpecialPropetyDeclarations(symbol); } else { type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.js b/tests/baselines/reference/inferringClassMembersFromAssignments.js new file mode 100644 index 0000000000..6513a3b03a --- /dev/null +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.js @@ -0,0 +1,150 @@ +//// [tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts] //// + +//// [a.js] + +class C { + constructor() { + if (Math.random()) { + this.inConstructor = 0; + } + else { + this.inConstructor = "string" + } + } + method() { + if (Math.random()) { + this.inMethod = 0; + } + else { + this.inMethod = "string" + } + } + get() { + if (Math.random()) { + this.inGetter = 0; + } + else { + this.inGetter = "string" + } + } + set() { + if (Math.random()) { + this.inSetter = 0; + } + else { + this.inSetter = "string" + } + } + static method() { + if (Math.random()) { + this.inStaticMethod = 0; + } + else { + this.inStaticMethod = "string" + } + } + static get() { + if (Math.random()) { + this.inStaticGetter = 0; + } + else { + this.inStaticGetter = "string" + } + } + static set() { + if (Math.random()) { + this.inStaticSetter = 0; + } + else { + this.inStaticSetter = "string" + } + } +} + +//// [b.ts] +var c = new C(); + +var stringOrNumber: string | number; +var stringOrNumber = c.inConstructor; + +var stringOrNumberOrUndefined: string | number | undefined; + +var stringOrNumberOrUndefined = c.inMethod; +var stringOrNumberOrUndefined = c.inGetter; +var stringOrNumberOrUndefined = c.inSetter; + +var stringOrNumberOrUndefined = C.inStaticMethod; +var stringOrNumberOrUndefined = C.inStaticGetter; +var stringOrNumberOrUndefined = C.inStaticSetter; + + +//// [output.js] +var C = (function () { + function C() { + if (Math.random()) { + this.inConstructor = 0; + } + else { + this.inConstructor = "string"; + } + } + C.prototype.method = function () { + if (Math.random()) { + this.inMethod = 0; + } + else { + this.inMethod = "string"; + } + }; + C.prototype.get = function () { + if (Math.random()) { + this.inGetter = 0; + } + else { + this.inGetter = "string"; + } + }; + C.prototype.set = function () { + if (Math.random()) { + this.inSetter = 0; + } + else { + this.inSetter = "string"; + } + }; + C.method = function () { + if (Math.random()) { + this.inStaticMethod = 0; + } + else { + this.inStaticMethod = "string"; + } + }; + C.get = function () { + if (Math.random()) { + this.inStaticGetter = 0; + } + else { + this.inStaticGetter = "string"; + } + }; + C.set = function () { + if (Math.random()) { + this.inStaticSetter = 0; + } + else { + this.inStaticSetter = "string"; + } + }; + return C; +}()); +var c = new C(); +var stringOrNumber; +var stringOrNumber = c.inConstructor; +var stringOrNumberOrUndefined; +var stringOrNumberOrUndefined = c.inMethod; +var stringOrNumberOrUndefined = c.inGetter; +var stringOrNumberOrUndefined = c.inSetter; +var stringOrNumberOrUndefined = C.inStaticMethod; +var stringOrNumberOrUndefined = C.inStaticGetter; +var stringOrNumberOrUndefined = C.inStaticSetter; diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.symbols b/tests/baselines/reference/inferringClassMembersFromAssignments.symbols new file mode 100644 index 0000000000..d96d4ff359 --- /dev/null +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.symbols @@ -0,0 +1,198 @@ +=== tests/cases/conformance/salsa/a.js === + +class C { +>C : Symbol(C, Decl(a.js, 0, 0)) + + constructor() { + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inConstructor = 0; +>this.inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) + } + else { + this.inConstructor = "string" +>this.inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) + } + } + method() { +>method : Symbol(C.method, Decl(a.js, 9, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inMethod = 0; +>this.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) + } + else { + this.inMethod = "string" +>this.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) + } + } + get() { +>get : Symbol(C.get, Decl(a.js, 17, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inGetter = 0; +>this.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) + } + else { + this.inGetter = "string" +>this.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) + } + } + set() { +>set : Symbol(C.set, Decl(a.js, 25, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inSetter = 0; +>this.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) + } + else { + this.inSetter = "string" +>this.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) + } + } + static method() { +>method : Symbol(C.method, Decl(a.js, 33, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inStaticMethod = 0; +>this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) + } + else { + this.inStaticMethod = "string" +>this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) + } + } + static get() { +>get : Symbol(C.get, Decl(a.js, 41, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inStaticGetter = 0; +>this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) + } + else { + this.inStaticGetter = "string" +>this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) + } + } + static set() { +>set : Symbol(C.set, Decl(a.js, 49, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + + this.inStaticSetter = 0; +>this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) + } + else { + this.inStaticSetter = "string" +>this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) + } + } +} + +=== tests/cases/conformance/salsa/b.ts === +var c = new C(); +>c : Symbol(c, Decl(b.ts, 0, 3)) +>C : Symbol(C, Decl(a.js, 0, 0)) + +var stringOrNumber: string | number; +>stringOrNumber : Symbol(stringOrNumber, Decl(b.ts, 2, 3), Decl(b.ts, 3, 3)) + +var stringOrNumber = c.inConstructor; +>stringOrNumber : Symbol(stringOrNumber, Decl(b.ts, 2, 3), Decl(b.ts, 3, 3)) +>c.inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) +>c : Symbol(c, Decl(b.ts, 0, 3)) +>inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) + +var stringOrNumberOrUndefined: string | number | undefined; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) + +var stringOrNumberOrUndefined = c.inMethod; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>c.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>c : Symbol(c, Decl(b.ts, 0, 3)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) + +var stringOrNumberOrUndefined = c.inGetter; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>c.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>c : Symbol(c, Decl(b.ts, 0, 3)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) + +var stringOrNumberOrUndefined = c.inSetter; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>c.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>c : Symbol(c, Decl(b.ts, 0, 3)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) + +var stringOrNumberOrUndefined = C.inStaticMethod; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>C.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>C : Symbol(C, Decl(a.js, 0, 0)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) + +var stringOrNumberOrUndefined = C.inStaticGetter; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>C.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>C : Symbol(C, Decl(a.js, 0, 0)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) + +var stringOrNumberOrUndefined = C.inStaticSetter; +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>C.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>C : Symbol(C, Decl(a.js, 0, 0)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) + diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.types b/tests/baselines/reference/inferringClassMembersFromAssignments.types new file mode 100644 index 0000000000..c1b30ae451 --- /dev/null +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.types @@ -0,0 +1,234 @@ +=== tests/cases/conformance/salsa/a.js === + +class C { +>C : C + + constructor() { + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inConstructor = 0; +>this.inConstructor = 0 : 0 +>this.inConstructor : string | number +>this : this +>inConstructor : string | number +>0 : 0 + } + else { + this.inConstructor = "string" +>this.inConstructor = "string" : "string" +>this.inConstructor : string | number +>this : this +>inConstructor : string | number +>"string" : "string" + } + } + method() { +>method : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inMethod = 0; +>this.inMethod = 0 : 0 +>this.inMethod : string | number | undefined +>this : this +>inMethod : string | number | undefined +>0 : 0 + } + else { + this.inMethod = "string" +>this.inMethod = "string" : "string" +>this.inMethod : string | number | undefined +>this : this +>inMethod : string | number | undefined +>"string" : "string" + } + } + get() { +>get : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inGetter = 0; +>this.inGetter = 0 : 0 +>this.inGetter : string | number | undefined +>this : this +>inGetter : string | number | undefined +>0 : 0 + } + else { + this.inGetter = "string" +>this.inGetter = "string" : "string" +>this.inGetter : string | number | undefined +>this : this +>inGetter : string | number | undefined +>"string" : "string" + } + } + set() { +>set : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inSetter = 0; +>this.inSetter = 0 : 0 +>this.inSetter : string | number | undefined +>this : this +>inSetter : string | number | undefined +>0 : 0 + } + else { + this.inSetter = "string" +>this.inSetter = "string" : "string" +>this.inSetter : string | number | undefined +>this : this +>inSetter : string | number | undefined +>"string" : "string" + } + } + static method() { +>method : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inStaticMethod = 0; +>this.inStaticMethod = 0 : 0 +>this.inStaticMethod : string | number | undefined +>this : typeof C +>inStaticMethod : string | number | undefined +>0 : 0 + } + else { + this.inStaticMethod = "string" +>this.inStaticMethod = "string" : "string" +>this.inStaticMethod : string | number | undefined +>this : typeof C +>inStaticMethod : string | number | undefined +>"string" : "string" + } + } + static get() { +>get : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inStaticGetter = 0; +>this.inStaticGetter = 0 : 0 +>this.inStaticGetter : string | number | undefined +>this : typeof C +>inStaticGetter : string | number | undefined +>0 : 0 + } + else { + this.inStaticGetter = "string" +>this.inStaticGetter = "string" : "string" +>this.inStaticGetter : string | number | undefined +>this : typeof C +>inStaticGetter : string | number | undefined +>"string" : "string" + } + } + static set() { +>set : () => void + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + this.inStaticSetter = 0; +>this.inStaticSetter = 0 : 0 +>this.inStaticSetter : string | number | undefined +>this : typeof C +>inStaticSetter : string | number | undefined +>0 : 0 + } + else { + this.inStaticSetter = "string" +>this.inStaticSetter = "string" : "string" +>this.inStaticSetter : string | number | undefined +>this : typeof C +>inStaticSetter : string | number | undefined +>"string" : "string" + } + } +} + +=== tests/cases/conformance/salsa/b.ts === +var c = new C(); +>c : C +>new C() : C +>C : typeof C + +var stringOrNumber: string | number; +>stringOrNumber : string | number + +var stringOrNumber = c.inConstructor; +>stringOrNumber : string | number +>c.inConstructor : string | number +>c : C +>inConstructor : string | number + +var stringOrNumberOrUndefined: string | number | undefined; +>stringOrNumberOrUndefined : string | number | undefined + +var stringOrNumberOrUndefined = c.inMethod; +>stringOrNumberOrUndefined : string | number | undefined +>c.inMethod : string | number | undefined +>c : C +>inMethod : string | number | undefined + +var stringOrNumberOrUndefined = c.inGetter; +>stringOrNumberOrUndefined : string | number | undefined +>c.inGetter : string | number | undefined +>c : C +>inGetter : string | number | undefined + +var stringOrNumberOrUndefined = c.inSetter; +>stringOrNumberOrUndefined : string | number | undefined +>c.inSetter : string | number | undefined +>c : C +>inSetter : string | number | undefined + +var stringOrNumberOrUndefined = C.inStaticMethod; +>stringOrNumberOrUndefined : string | number | undefined +>C.inStaticMethod : string | number | undefined +>C : typeof C +>inStaticMethod : string | number | undefined + +var stringOrNumberOrUndefined = C.inStaticGetter; +>stringOrNumberOrUndefined : string | number | undefined +>C.inStaticGetter : string | number | undefined +>C : typeof C +>inStaticGetter : string | number | undefined + +var stringOrNumberOrUndefined = C.inStaticSetter; +>stringOrNumberOrUndefined : string | number | undefined +>C.inStaticSetter : string | number | undefined +>C : typeof C +>inStaticSetter : string | number | undefined + diff --git a/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts b/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts new file mode 100644 index 0000000000..dc63f09b3a --- /dev/null +++ b/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts @@ -0,0 +1,79 @@ +// @out: output.js +// @allowJs: true +// @strictNullChecks: true + +// @filename: a.js +class C { + constructor() { + if (Math.random()) { + this.inConstructor = 0; + } + else { + this.inConstructor = "string" + } + } + method() { + if (Math.random()) { + this.inMethod = 0; + } + else { + this.inMethod = "string" + } + } + get() { + if (Math.random()) { + this.inGetter = 0; + } + else { + this.inGetter = "string" + } + } + set() { + if (Math.random()) { + this.inSetter = 0; + } + else { + this.inSetter = "string" + } + } + static method() { + if (Math.random()) { + this.inStaticMethod = 0; + } + else { + this.inStaticMethod = "string" + } + } + static get() { + if (Math.random()) { + this.inStaticGetter = 0; + } + else { + this.inStaticGetter = "string" + } + } + static set() { + if (Math.random()) { + this.inStaticSetter = 0; + } + else { + this.inStaticSetter = "string" + } + } +} + +// @filename: b.ts +var c = new C(); + +var stringOrNumber: string | number; +var stringOrNumber = c.inConstructor; + +var stringOrNumberOrUndefined: string | number | undefined; + +var stringOrNumberOrUndefined = c.inMethod; +var stringOrNumberOrUndefined = c.inGetter; +var stringOrNumberOrUndefined = c.inSetter; + +var stringOrNumberOrUndefined = C.inStaticMethod; +var stringOrNumberOrUndefined = C.inStaticGetter; +var stringOrNumberOrUndefined = C.inStaticSetter; From b977b8cc45d9c5fee51334507b95124cc393ab3b Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 27 Feb 2017 15:58:01 -0800 Subject: [PATCH 2/3] Respond to code review comments --- src/compiler/binder.ts | 5 +- src/compiler/checker.ts | 4 +- .../inferringClassMembersFromAssignments.js | 13 ++ ...ferringClassMembersFromAssignments.symbols | 120 +++++++++++------- ...inferringClassMembersFromAssignments.types | 28 ++++ .../inferringClassMembersFromAssignments.ts | 8 ++ 6 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2fc7317884..beba322496 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2254,10 +2254,7 @@ namespace ts { // this.foo assignment in a JavaScript class // Bind this property to the containing class const containingClass = container.parent; - const symbol = hasModifier(container, ModifierFlags.Static) - ? declareSymbol(containingClass.symbol.exports, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None) - : declareSymbol(containingClass.symbol.members, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None); - + const symbol = declareSymbol(hasModifier(container, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None); if (symbol) { // symbols declared through 'this' property assignements can be overwritten by subsequent method declarations (symbol as Symbol).isReplaceableByMethod = true; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 93973fe71a..aa8d42dd3c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3472,7 +3472,7 @@ namespace ts { return undefined; } - function getWidendedTypeFromJSSpecialPropetyDeclarations(symbol: Symbol) { + function getWidenedTypeFromJSSpecialPropetyDeclarations(symbol: Symbol) { const types: Type[] = []; let definedInConstructor = false; let definedInMethod = false; @@ -3663,7 +3663,7 @@ namespace ts { // * className.prototype.method = expr if (declaration.kind === SyntaxKind.BinaryExpression || declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { - type = getWidendedTypeFromJSSpecialPropetyDeclarations(symbol); + type = getWidenedTypeFromJSSpecialPropetyDeclarations(symbol); } else { type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.js b/tests/baselines/reference/inferringClassMembersFromAssignments.js index 6513a3b03a..a7b9e4ff8f 100644 --- a/tests/baselines/reference/inferringClassMembersFromAssignments.js +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.js @@ -10,6 +10,7 @@ class C { else { this.inConstructor = "string" } + this.inMultiple = 0; } method() { if (Math.random()) { @@ -18,6 +19,7 @@ class C { else { this.inMethod = "string" } + this.inMultiple = "string"; } get() { if (Math.random()) { @@ -26,6 +28,7 @@ class C { else { this.inGetter = "string" } + this.inMultiple = false; } set() { if (Math.random()) { @@ -73,6 +76,11 @@ var stringOrNumberOrUndefined = c.inMethod; var stringOrNumberOrUndefined = c.inGetter; var stringOrNumberOrUndefined = c.inSetter; +var stringOrNumberOrBoolean: string | number | boolean; + +var stringOrNumberOrBoolean = c.inMultiple; + + var stringOrNumberOrUndefined = C.inStaticMethod; var stringOrNumberOrUndefined = C.inStaticGetter; var stringOrNumberOrUndefined = C.inStaticSetter; @@ -87,6 +95,7 @@ var C = (function () { else { this.inConstructor = "string"; } + this.inMultiple = 0; } C.prototype.method = function () { if (Math.random()) { @@ -95,6 +104,7 @@ var C = (function () { else { this.inMethod = "string"; } + this.inMultiple = "string"; }; C.prototype.get = function () { if (Math.random()) { @@ -103,6 +113,7 @@ var C = (function () { else { this.inGetter = "string"; } + this.inMultiple = false; }; C.prototype.set = function () { if (Math.random()) { @@ -145,6 +156,8 @@ var stringOrNumberOrUndefined; var stringOrNumberOrUndefined = c.inMethod; var stringOrNumberOrUndefined = c.inGetter; var stringOrNumberOrUndefined = c.inSetter; +var stringOrNumberOrBoolean; +var stringOrNumberOrBoolean = c.inMultiple; var stringOrNumberOrUndefined = C.inStaticMethod; var stringOrNumberOrUndefined = C.inStaticGetter; var stringOrNumberOrUndefined = C.inStaticSetter; diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.symbols b/tests/baselines/reference/inferringClassMembersFromAssignments.symbols index d96d4ff359..9951339f8a 100644 --- a/tests/baselines/reference/inferringClassMembersFromAssignments.symbols +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.symbols @@ -20,9 +20,13 @@ class C { >this : Symbol(C, Decl(a.js, 0, 0)) >inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) } + this.inMultiple = 0; +>this.inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) } method() { ->method : Symbol(C.method, Decl(a.js, 9, 5)) +>method : Symbol(C.method, Decl(a.js, 10, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -30,19 +34,23 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inMethod = 0; ->this.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>this.inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) } else { this.inMethod = "string" ->this.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>this.inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) } + this.inMultiple = "string"; +>this.inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) } get() { ->get : Symbol(C.get, Decl(a.js, 17, 5)) +>get : Symbol(C.get, Decl(a.js, 19, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -50,19 +58,23 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inGetter = 0; ->this.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>this.inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) } else { this.inGetter = "string" ->this.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>this.inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) } + this.inMultiple = false; +>this.inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) +>this : Symbol(C, Decl(a.js, 0, 0)) +>inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) } set() { ->set : Symbol(C.set, Decl(a.js, 25, 5)) +>set : Symbol(C.set, Decl(a.js, 28, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -70,19 +82,19 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inSetter = 0; ->this.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>this.inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) } else { this.inSetter = "string" ->this.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>this.inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) } } static method() { ->method : Symbol(C.method, Decl(a.js, 33, 5)) +>method : Symbol(C.method, Decl(a.js, 36, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -90,19 +102,19 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inStaticMethod = 0; ->this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) } else { this.inStaticMethod = "string" ->this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>this.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) } } static get() { ->get : Symbol(C.get, Decl(a.js, 41, 5)) +>get : Symbol(C.get, Decl(a.js, 44, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -110,19 +122,19 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inStaticGetter = 0; ->this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) } else { this.inStaticGetter = "string" ->this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>this.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) } } static set() { ->set : Symbol(C.set, Decl(a.js, 49, 5)) +>set : Symbol(C.set, Decl(a.js, 52, 5)) if (Math.random()) { >Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) @@ -130,15 +142,15 @@ class C { >random : Symbol(Math.random, Decl(lib.d.ts, --, --)) this.inStaticSetter = 0; ->this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) } else { this.inStaticSetter = "string" ->this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>this.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) >this : Symbol(C, Decl(a.js, 0, 0)) ->inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) } } } @@ -158,41 +170,51 @@ var stringOrNumber = c.inConstructor; >inConstructor : Symbol(C.inConstructor, Decl(a.js, 3, 28), Decl(a.js, 6, 14)) var stringOrNumberOrUndefined: string | number | undefined; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) var stringOrNumberOrUndefined = c.inMethod; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->c.inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>c.inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) >c : Symbol(c, Decl(b.ts, 0, 3)) ->inMethod : Symbol(C.inMethod, Decl(a.js, 11, 28), Decl(a.js, 14, 14)) +>inMethod : Symbol(C.inMethod, Decl(a.js, 12, 28), Decl(a.js, 15, 14)) var stringOrNumberOrUndefined = c.inGetter; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->c.inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>c.inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) >c : Symbol(c, Decl(b.ts, 0, 3)) ->inGetter : Symbol(C.inGetter, Decl(a.js, 19, 28), Decl(a.js, 22, 14)) +>inGetter : Symbol(C.inGetter, Decl(a.js, 21, 28), Decl(a.js, 24, 14)) var stringOrNumberOrUndefined = c.inSetter; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->c.inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>c.inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) >c : Symbol(c, Decl(b.ts, 0, 3)) ->inSetter : Symbol(C.inSetter, Decl(a.js, 27, 28), Decl(a.js, 30, 14)) +>inSetter : Symbol(C.inSetter, Decl(a.js, 30, 28), Decl(a.js, 33, 14)) + +var stringOrNumberOrBoolean: string | number | boolean; +>stringOrNumberOrBoolean : Symbol(stringOrNumberOrBoolean, Decl(b.ts, 11, 3), Decl(b.ts, 13, 3)) + +var stringOrNumberOrBoolean = c.inMultiple; +>stringOrNumberOrBoolean : Symbol(stringOrNumberOrBoolean, Decl(b.ts, 11, 3), Decl(b.ts, 13, 3)) +>c.inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) +>c : Symbol(c, Decl(b.ts, 0, 3)) +>inMultiple : Symbol(C.inMultiple, Decl(a.js, 8, 9), Decl(a.js, 17, 9), Decl(a.js, 26, 9)) + var stringOrNumberOrUndefined = C.inStaticMethod; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->C.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>C.inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) >C : Symbol(C, Decl(a.js, 0, 0)) ->inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 35, 28), Decl(a.js, 38, 14)) +>inStaticMethod : Symbol(C.inStaticMethod, Decl(a.js, 38, 28), Decl(a.js, 41, 14)) var stringOrNumberOrUndefined = C.inStaticGetter; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->C.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>C.inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) >C : Symbol(C, Decl(a.js, 0, 0)) ->inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 43, 28), Decl(a.js, 46, 14)) +>inStaticGetter : Symbol(C.inStaticGetter, Decl(a.js, 46, 28), Decl(a.js, 49, 14)) var stringOrNumberOrUndefined = C.inStaticSetter; ->stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 11, 3), Decl(b.ts, 12, 3), Decl(b.ts, 13, 3)) ->C.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>stringOrNumberOrUndefined : Symbol(stringOrNumberOrUndefined, Decl(b.ts, 5, 3), Decl(b.ts, 7, 3), Decl(b.ts, 8, 3), Decl(b.ts, 9, 3), Decl(b.ts, 16, 3), Decl(b.ts, 17, 3), Decl(b.ts, 18, 3)) +>C.inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) >C : Symbol(C, Decl(a.js, 0, 0)) ->inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 51, 28), Decl(a.js, 54, 14)) +>inStaticSetter : Symbol(C.inStaticSetter, Decl(a.js, 54, 28), Decl(a.js, 57, 14)) diff --git a/tests/baselines/reference/inferringClassMembersFromAssignments.types b/tests/baselines/reference/inferringClassMembersFromAssignments.types index c1b30ae451..547cc62ba3 100644 --- a/tests/baselines/reference/inferringClassMembersFromAssignments.types +++ b/tests/baselines/reference/inferringClassMembersFromAssignments.types @@ -25,6 +25,12 @@ class C { >inConstructor : string | number >"string" : "string" } + this.inMultiple = 0; +>this.inMultiple = 0 : 0 +>this.inMultiple : string | number | boolean +>this : this +>inMultiple : string | number | boolean +>0 : 0 } method() { >method : () => void @@ -50,6 +56,12 @@ class C { >inMethod : string | number | undefined >"string" : "string" } + this.inMultiple = "string"; +>this.inMultiple = "string" : "string" +>this.inMultiple : string | number | boolean +>this : this +>inMultiple : string | number | boolean +>"string" : "string" } get() { >get : () => void @@ -75,6 +87,12 @@ class C { >inGetter : string | number | undefined >"string" : "string" } + this.inMultiple = false; +>this.inMultiple = false : false +>this.inMultiple : string | number | boolean +>this : this +>inMultiple : string | number | boolean +>false : false } set() { >set : () => void @@ -214,6 +232,16 @@ var stringOrNumberOrUndefined = c.inSetter; >c : C >inSetter : string | number | undefined +var stringOrNumberOrBoolean: string | number | boolean; +>stringOrNumberOrBoolean : string | number | boolean + +var stringOrNumberOrBoolean = c.inMultiple; +>stringOrNumberOrBoolean : string | number | boolean +>c.inMultiple : string | number | boolean +>c : C +>inMultiple : string | number | boolean + + var stringOrNumberOrUndefined = C.inStaticMethod; >stringOrNumberOrUndefined : string | number | undefined >C.inStaticMethod : string | number | undefined diff --git a/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts b/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts index dc63f09b3a..d843e86b72 100644 --- a/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts +++ b/tests/cases/conformance/salsa/inferringClassMembersFromAssignments.ts @@ -11,6 +11,7 @@ class C { else { this.inConstructor = "string" } + this.inMultiple = 0; } method() { if (Math.random()) { @@ -19,6 +20,7 @@ class C { else { this.inMethod = "string" } + this.inMultiple = "string"; } get() { if (Math.random()) { @@ -27,6 +29,7 @@ class C { else { this.inGetter = "string" } + this.inMultiple = false; } set() { if (Math.random()) { @@ -74,6 +77,11 @@ var stringOrNumberOrUndefined = c.inMethod; var stringOrNumberOrUndefined = c.inGetter; var stringOrNumberOrUndefined = c.inSetter; +var stringOrNumberOrBoolean: string | number | boolean; + +var stringOrNumberOrBoolean = c.inMultiple; + + var stringOrNumberOrUndefined = C.inStaticMethod; var stringOrNumberOrUndefined = C.inStaticGetter; var stringOrNumberOrUndefined = C.inStaticSetter; From 8d9692129f42eee650c1b69be98b8a970b79e0e9 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 6 Mar 2017 16:53:39 -0800 Subject: [PATCH 3/3] Fix typo --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aa8d42dd3c..0db2a79271 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3472,7 +3472,7 @@ namespace ts { return undefined; } - function getWidenedTypeFromJSSpecialPropetyDeclarations(symbol: Symbol) { + function getWidenedTypeFromJSSpecialPropertyDeclarations(symbol: Symbol) { const types: Type[] = []; let definedInConstructor = false; let definedInMethod = false; @@ -3663,7 +3663,7 @@ namespace ts { // * className.prototype.method = expr if (declaration.kind === SyntaxKind.BinaryExpression || declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { - type = getWidenedTypeFromJSSpecialPropetyDeclarations(symbol); + type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol); } else { type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);