diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c763f9817e0e0..38a0cb8079766 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -88,7 +88,7 @@ module ts { symbolToString: symbolToString, symbolToDisplayParts: symbolToDisplayParts, getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType, - getRootSymbol: getRootSymbol, + getRootSymbols: getRootSymbols, getContextualType: getContextualType, getFullyQualifiedName: getFullyQualifiedName, getResolvedSignature: getResolvedSignature, @@ -2144,6 +2144,14 @@ module ts { } var symbol = createSymbol(SymbolFlags.UnionProperty | SymbolFlags.Transient, prop.name); symbol.unionType = type; + + symbol.declarations = []; + for (var i = 0; i < types.length; i++) { + var s = getPropertyOfType(types[i], prop.name); + if (s.declarations) + symbol.declarations.push.apply(symbol.declarations, s.declarations); + } + members[prop.name] = symbol; }); var callSignatures = getUnionSignatures(types, SignatureKind.Call); @@ -3635,7 +3643,8 @@ module ts { } function getBestCommonType(types: Type[], contextualType: Type): Type { - return contextualType && isSupertypeOfEach(contextualType, types) ? contextualType : getUnionType(types); } + return contextualType && isSupertypeOfEach(contextualType, types) ? contextualType : getUnionType(types); + } function isTypeOfObjectLiteral(type: Type): boolean { return (type.flags & TypeFlags.Anonymous) && type.symbol && (type.symbol.flags & SymbolFlags.ObjectLiteral) ? true : false; @@ -7928,8 +7937,22 @@ module ts { } } - function getRootSymbol(symbol: Symbol) { - return ((symbol.flags & SymbolFlags.Transient) && getSymbolLinks(symbol).target) || symbol; + function getRootSymbols(symbol: Symbol): Symbol[] { + if (symbol.flags & SymbolFlags.UnionProperty) { + var symbols: Symbol[] = []; + var name = symbol.name; + forEach(getSymbolLinks(symbol).unionType.types, t => { + symbols.push(getPropertyOfType(getApparentType(t), name)); + }); + return symbols; + } + else if (symbol.flags & SymbolFlags.Transient) { + var target = getSymbolLinks(symbol).target; + if (target) { + return [target]; + } + } + return [symbol]; } // Emitter support diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8d1b684d611d..99b6825281c27 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -653,7 +653,7 @@ module ts { symbolToDisplayParts(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): SymbolDisplayPart[]; getFullyQualifiedName(symbol: Symbol): string; getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; - getRootSymbol(symbol: Symbol): Symbol; + getRootSymbols(symbol: Symbol): Symbol[]; getContextualType(node: Node): Type; getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature; diff --git a/src/services/services.ts b/src/services/services.ts index 703a05c62cba3..539c154f6b82d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2322,7 +2322,7 @@ module ts { } function getSymbolKind(symbol: Symbol): string { - var flags = typeInfoResolver.getRootSymbol(symbol).getFlags(); + var flags = typeInfoResolver.getRootSymbols(symbol)[0].getFlags(); if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; @@ -2404,31 +2404,34 @@ module ts { var documentationParts = getSymbolDocumentationDisplayParts(symbol); + // TODO: handle union properties appropriately when merging with master + var symbolFlags = typeInfoResolver.getRootSymbols(symbol)[0].flags; + // Having all this logic here is pretty unclean. Consider moving to the roslyn model // where all symbol display logic is encapsulated into visitors and options. var totalParts: SymbolDisplayPart[] = []; - if (symbol.flags & SymbolFlags.Class) { + if (symbolFlags & SymbolFlags.Class) { totalParts.push(keywordPart(SyntaxKind.ClassKeyword)); totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.Interface) { + else if (symbolFlags & SymbolFlags.Interface) { totalParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.Enum) { + else if (symbolFlags & SymbolFlags.Enum) { totalParts.push(keywordPart(SyntaxKind.EnumKeyword)); totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.Module) { + else if (symbolFlags & SymbolFlags.Module) { totalParts.push(keywordPart(SyntaxKind.ModuleKeyword)); totalParts.push(spacePart()); totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, sourceFile)); } - else if (symbol.flags & SymbolFlags.TypeParameter) { + else if (symbolFlags & SymbolFlags.TypeParameter) { totalParts.push(punctuationPart(SyntaxKind.OpenParenToken)); totalParts.push(new SymbolDisplayPart("type parameter", SymbolDisplayPartKind.text, undefined)); totalParts.push(punctuationPart(SyntaxKind.CloseParenToken)); @@ -2439,11 +2442,11 @@ module ts { totalParts.push(punctuationPart(SyntaxKind.OpenParenToken)); var text: string; - if (symbol.flags & SymbolFlags.Property) { text = "property" } - else if (symbol.flags & SymbolFlags.EnumMember) { text = "enum member" } - else if (symbol.flags & SymbolFlags.Function) { text = "function" } - else if (symbol.flags & SymbolFlags.Variable) { text = "variable" } - else if (symbol.flags & SymbolFlags.Method) { text = "method" } + if (symbolFlags & SymbolFlags.Property) { text = "property" } + else if (symbolFlags & SymbolFlags.EnumMember) { text = "enum member" } + else if (symbolFlags & SymbolFlags.Function) { text = "function" } + else if (symbolFlags & SymbolFlags.Variable) { text = "variable" } + else if (symbolFlags & SymbolFlags.Method) { text = "method" } if (!text) { return undefined; @@ -2457,8 +2460,8 @@ module ts { var type = typeInfoResolver.getTypeOfSymbol(symbol); - if (symbol.flags & SymbolFlags.Property || - symbol.flags & SymbolFlags.Variable) { + if (symbolFlags & SymbolFlags.Property || + symbolFlags & SymbolFlags.Variable) { if (type) { totalParts.push(punctuationPart(SyntaxKind.ColonToken)); @@ -2466,13 +2469,13 @@ module ts { totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node))); } } - else if (symbol.flags & SymbolFlags.Function || - symbol.flags & SymbolFlags.Method) { + else if (symbolFlags & SymbolFlags.Function || + symbolFlags & SymbolFlags.Method) { if (type) { totalParts.push.apply(totalParts, typeInfoResolver.typeToDisplayParts(type, getContainerNode(node))); } } - else if (symbol.flags & SymbolFlags.EnumMember) { + else if (symbolFlags & SymbolFlags.EnumMember) { var declaration = symbol.declarations[0]; if (declaration.kind === SyntaxKind.EnumMember) { var constantValue = typeInfoResolver.getEnumMemberValue(declaration); @@ -2521,7 +2524,7 @@ module ts { } /// Goto definition - function getDefinitionAtPosition(filename: string, position: number): DefinitionInfo[]{ + function getDefinitionAtPosition(filename: string, position: number): DefinitionInfo[] { function getDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { return { fileName: node.getSourceFile().filename, @@ -2553,7 +2556,7 @@ module ts { result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); return true; } - + return false; } @@ -2578,6 +2581,25 @@ module ts { return false; } + function getDefinitionFromSymbol(symbol: Symbol, location: Node, result: DefinitionInfo[]): void { + var declarations = symbol.getDeclarations(); + if (declarations) { + var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + var symbolKind = getSymbolKind(symbol); + var containerSymbol = symbol.parent; + var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, location) : ""; + var containerKind = containerSymbol ? getSymbolKind(symbol) : ""; + + if (!tryAddConstructSignature(symbol, location, symbolKind, symbolName, containerName, result) && + !tryAddCallSignature(symbol, location, symbolKind, symbolName, containerName, result)) { + // Just add all the declarations. + forEach(declarations, declaration => { + result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + }); + } + } + } + synchronizeHostData(); filename = TypeScript.switchToForwardSlashes(filename); @@ -2617,26 +2639,13 @@ module ts { // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol - if (!symbol || !(symbol.getDeclarations())) { + if (!symbol) { return undefined; } var result: DefinitionInfo[] = []; - var declarations = symbol.getDeclarations(); - var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - var symbolKind = getSymbolKind(symbol); - var containerSymbol = symbol.parent; - var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - var containerKind = containerSymbol ? getSymbolKind(symbol) : ""; - - if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && - !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. - forEach(declarations, declaration => { - result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - }); - } + getDefinitionFromSymbol(symbol, node, result); return result; } @@ -3151,18 +3160,20 @@ module ts { return [getReferenceEntryFromNode(node)]; } - // the symbol was an internal symbol and does not have a declaration e.g.undefined symbol - if (!symbol.getDeclarations()) { + var declarations = symbol.declarations; + + // The symbol was an internal symbol and does not have a declaration e.g.undefined symbol + if (!declarations || !declarations.length) { return undefined; } var result: ReferenceEntry[]; // Compute the meaning from the location and the symbol it references - var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.getDeclarations()); + var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations); // Get the text to search for, we need to normalize it as external module names will have quote - var symbolName = getNormalizedSymbolName(symbol); + var symbolName = getNormalizedSymbolName(symbol.name, declarations); // Get syntactic diagnostics var scope = getSymbolScope(symbol); @@ -3184,15 +3195,15 @@ module ts { return result; - function getNormalizedSymbolName(symbol: Symbol): string { + function getNormalizedSymbolName(symbolName: string, declarations: Declaration[]): string { // Special case for function expressions, whose names are solely local to their bodies. - var functionExpression = getDeclarationOfKind(symbol, SyntaxKind.FunctionExpression); + var functionExpression = forEach(declarations, d => d.kind === SyntaxKind.FunctionExpression ? d : undefined); if (functionExpression && functionExpression.name) { var name = functionExpression.name.text; } else { - var name = symbol.name; + var name = symbolName; } var length = name.length; @@ -3219,22 +3230,24 @@ module ts { var scope: Node = undefined; var declarations = symbol.getDeclarations(); - for (var i = 0, n = declarations.length; i < n; i++) { - var container = getContainerNode(declarations[i]); + if (declarations) { + for (var i = 0, n = declarations.length; i < n; i++) { + var container = getContainerNode(declarations[i]); - if (scope && scope !== container) { - // Different declarations have different containers, bail out - return undefined; - } + if (scope && scope !== container) { + // Different declarations have different containers, bail out + return undefined; + } - if (container.kind === SyntaxKind.SourceFile && !isExternalModule(container)) { - // This is a global variable and not an external module, any declaration defined - // within this scope is visible outside the file - return undefined; - } + if (container.kind === SyntaxKind.SourceFile && !isExternalModule(container)) { + // This is a global variable and not an external module, any declaration defined + // within this scope is visible outside the file + return undefined; + } - // The search scope is the container node - scope = container; + // The search scope is the container node + scope = container; + } } return scope; @@ -3370,14 +3383,7 @@ module ts { } var referenceSymbol = typeInfoResolver.getSymbolInfo(referenceLocation); - - // Could not find a symbol e.g. node is string or number keyword, - // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol - if (!referenceSymbol || !(referenceSymbol.getDeclarations())) { - return; - } - - if (isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) { + if (referenceSymbol && isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation)) { result.push(getReferenceEntryFromNode(referenceLocation)); } }); @@ -3539,24 +3545,27 @@ module ts { // The search set contains at least the current symbol var result = [symbol]; - // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list - var rootSymbol = typeInfoResolver.getRootSymbol(symbol); - if (rootSymbol && rootSymbol !== symbol) { - result.push(rootSymbol); - } - // If the location is in a context sensitive location (i.e. in an object literal) try // to get a contextual type for it, and add the property symbol from the contextual // type to the search set if (isNameOfPropertyAssignment(location)) { - var symbolFromContextualType = getPropertySymbolFromContextualType(location); - if (symbolFromContextualType) result.push(typeInfoResolver.getRootSymbol(symbolFromContextualType)); + forEach(getPropertySymbolsFromContextualType(location), contextualSymbol => { + result.push.apply(result, typeInfoResolver.getRootSymbols(contextualSymbol)); + }); } - // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions - if (symbol.parent && symbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - getPropertySymbolsFromBaseTypes(symbol.parent, symbol.getName(), result); - } + // If this is a union property, add all the symbols from all its source symbols in all unioned types. + // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list + forEach(typeInfoResolver.getRootSymbols(symbol), rootSymbol => { + if (rootSymbol !== symbol) { + result.push(rootSymbol); + } + + // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions + if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); + } + }); return result; } @@ -3592,11 +3601,7 @@ module ts { } function isRelatableToSearchSet(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean { - // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) - var referenceSymbolTarget = typeInfoResolver.getRootSymbol(referenceSymbol); - - // if it is in the list, then we are done - if (searchSymbols.indexOf(referenceSymbolTarget) >= 0) { + if (searchSymbols.indexOf(referenceSymbol) >= 0) { return true; } @@ -3604,29 +3609,61 @@ module ts { // object literal, lookup the property symbol in the contextual type, and use this symbol to // compare to our searchSymbol if (isNameOfPropertyAssignment(referenceLocation)) { - var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation); - if (symbolFromContextualType && searchSymbols.indexOf(typeInfoResolver.getRootSymbol(symbolFromContextualType)) >= 0) { + return forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => { + return forEach(typeInfoResolver.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0); + }); + } + + // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) + // Or a union property, use its underlying unioned symbols + return forEach(typeInfoResolver.getRootSymbols(referenceSymbol), rootSymbol => { + // if it is in the list, then we are done + if (searchSymbols.indexOf(rootSymbol) >= 0) { return true; } - } - // Finally, try all properties with the same name in any type the containing type extend or implemented, and - // see if any is in the list - if (referenceSymbol.parent && referenceSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - var result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(referenceSymbol.parent, referenceSymbol.getName(), result); - return forEach(result, s => searchSymbols.indexOf(s) >= 0); - } + // Finally, try all properties with the same name in any type the containing type extended or implemented, and + // see if any is in the list + if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + var result: Symbol[] = []; + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); + return forEach(result, s => searchSymbols.indexOf(s) >= 0); + } - return false; + return false; + }); } - function getPropertySymbolFromContextualType(node: Node): Symbol { + function getPropertySymbolsFromContextualType(node: Node): Symbol[] { if (isNameOfPropertyAssignment(node)) { var objectLiteral = node.parent.parent; var contextualType = typeInfoResolver.getContextualType(objectLiteral); + var name = (node).text; if (contextualType) { - return typeInfoResolver.getPropertyOfType(contextualType, (node).text); + if (contextualType.flags & TypeFlags.Union) { + // This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types) + // if not, search the constituent types for the property + var unionProperty = contextualType.getProperty(name) + if (unionProperty) { + return [unionProperty]; + } + else { + var result: Symbol[] = []; + forEach((contextualType).types, t => { + var symbol = t.getProperty(name); + if (symbol) { + result.push(symbol); + } + }); + return result; + } + } + else { + var symbol = contextualType.getProperty(name); + if (symbol) { + return [symbol]; + } + } } } return undefined; diff --git a/tests/cases/fourslash/completionEntryForUnionProperty.ts b/tests/cases/fourslash/completionEntryForUnionProperty.ts new file mode 100644 index 0000000000000..6d5a3627a9c4a --- /dev/null +++ b/tests/cases/fourslash/completionEntryForUnionProperty.ts @@ -0,0 +1,20 @@ +/// + +////interface One { +//// commonProperty: number; +//// commonFunction(): number; +////} +//// +////interface Two { +//// commonProperty: string +//// commonFunction(): number; +////} +//// +////var x : One | Two; +//// +////x./**/ + +goTo.marker(); +verify.memberListContains("commonProperty", "string | number", undefined, undefined, "property"); +verify.memberListContains("commonFunction", "() => number", undefined, undefined, "method"); +verify.memberListCount(2); \ No newline at end of file diff --git a/tests/cases/fourslash/completionEntryForUnionProperty2.ts b/tests/cases/fourslash/completionEntryForUnionProperty2.ts new file mode 100644 index 0000000000000..fef5e0dfb5051 --- /dev/null +++ b/tests/cases/fourslash/completionEntryForUnionProperty2.ts @@ -0,0 +1,19 @@ +/// + +////interface One { +//// commonProperty: number; +//// commonFunction(): number; +////} +//// +////interface Two { +//// commonProperty: string +//// commonFunction(): number; +////} +//// +////var x : One | Two; +//// +////x.commonProperty./**/ + +goTo.marker(); +verify.memberListContains("toString", "() => string", undefined, undefined, "method"); +verify.memberListCount(1); \ No newline at end of file diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts new file mode 100644 index 0000000000000..74b5e2aad8a9f --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts @@ -0,0 +1,24 @@ +/// + +////interface One { +//// /*propertyDefinition1*/commonProperty: number; +//// commonFunction(): number; +////} +//// +////interface Two { +//// /*propertyDefinition2*/commonProperty: string +//// commonFunction(): number; +////} +//// +////var x : One | Two; +//// +////x./*propertyReference*/commonProperty; +////x./*3*/commonFunction; + +goTo.marker("propertyReference"); +goTo.definition(0); +verify.caretAtMarker("propertyDefinition1"); + +goTo.marker("propertyReference"); +goTo.definition(1); +verify.caretAtMarker("propertyDefinition2"); diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts new file mode 100644 index 0000000000000..6536bb76e7dda --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts @@ -0,0 +1,25 @@ +/// +////interface HasAOrB { +//// /*propertyDefinition1*/a: string; +//// b: string; +////} +//// +////interface One { +//// common: { /*propertyDefinition2*/a : number; }; +////} +//// +////interface Two { +//// common: HasAOrB; +////} +//// +////var x : One | Two; +//// +////x.common./*propertyReference*/a; + +goTo.marker("propertyReference"); +goTo.definition(0); +verify.caretAtMarker("propertyDefinition1"); + +goTo.marker("propertyReference"); +goTo.definition(1); +verify.caretAtMarker("propertyDefinition2"); diff --git a/tests/cases/fourslash/quickinfoForUnionProperty.ts b/tests/cases/fourslash/quickinfoForUnionProperty.ts new file mode 100644 index 0000000000000..665d03f94357b --- /dev/null +++ b/tests/cases/fourslash/quickinfoForUnionProperty.ts @@ -0,0 +1,27 @@ +/// + +////interface One { +//// commonProperty: number; +//// commonFunction(): number; +////} +//// +////interface Two { +//// commonProperty: string +//// commonFunction(): number; +////} +//// +////var /*1*/x : One | Two; +//// +////x./*2*/commonProperty; +////x./*3*/commonFunction; + + +goTo.marker("1"); +verify.quickInfoIs("One | Two", "", "x", "var"); + + +goTo.marker("2"); +verify.quickInfoIs("string | number", "", "commonProperty", "property"); + +goTo.marker("3"); +verify.quickInfoIs("() => number", "", "commonFunction", "method"); diff --git a/tests/cases/fourslash/referencesForContextuallyTypedObjectLiteralProperties.ts b/tests/cases/fourslash/referencesForContextuallyTypedObjectLiteralProperties.ts index f21f6cc3936e2..15dd57200194c 100644 --- a/tests/cases/fourslash/referencesForContextuallyTypedObjectLiteralProperties.ts +++ b/tests/cases/fourslash/referencesForContextuallyTypedObjectLiteralProperties.ts @@ -1,5 +1,5 @@ -/// - +/// + ////interface IFoo { /*1*/xy: number; } //// ////// Assignment @@ -23,10 +23,10 @@ ////var w: IFoo = { /*4*/xy: undefined }; //// ////// Untped -- should not be included -////var u = { xy: 0 }; - - -test.markers().forEach((m) => { - goTo.position(m.position, m.fileName); - verify.referencesCountIs(9); -}); +////var u = { xy: 0 }; + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(9); +}); diff --git a/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties.ts b/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties.ts new file mode 100644 index 0000000000000..f9c479de7698f --- /dev/null +++ b/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties.ts @@ -0,0 +1,40 @@ +/// + +////interface A { +//// a: number; +//// common: string; +////} +//// +////interface B { +//// b: number; +//// common: number; +////} +//// +////// Assignment +////var v1: A | B = { a: 0, /*1*/common: "" }; +////var v2: A | B = { b: 0, /*2*/common: 3 }; +//// +////// Function call +////function consumer(f: A | B) { } +////consumer({ a: 0, b: 0, /*3*/common: 1 }); +//// +////// Type cast +////var c = { /*4*/common: 0, b: 0 }; +//// +////// Array literal +////var ar: Array = [{ a: 0, /*5*/common: "" }, { b: 0, /*6*/common: 0 }]; +//// +////// Nested object literal +////var ob: { aorb: A|B } = { aorb: { b: 0, /*7*/common: 0 } }; +//// +////// Widened type +////var w: A|B = { a:0, /*8*/common: undefined }; +//// +////// Untped -- should not be included +////var u1 = { a: 0, b: 0, common: "" }; +////var u2 = { b: 0, common: 0 }; + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(10); // 8 contextually typed common, and 2 in definition (A.common, B.common) +}); diff --git a/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties2.ts b/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties2.ts new file mode 100644 index 0000000000000..bbcc482a6e7ed --- /dev/null +++ b/tests/cases/fourslash/referencesForContextuallyTypedUnionProperties2.ts @@ -0,0 +1,40 @@ +/// + +////interface A { +//// a: number; +//// common: string; +////} +//// +////interface B { +//// /*1*/b: number; +//// common: number; +////} +//// +////// Assignment +////var v1: A | B = { a: 0, common: "" }; +////var v2: A | B = { /*2*/b: 0, common: 3 }; +//// +////// Function call +////function consumer(f: A | B) { } +////consumer({ a: 0, /*3*/b: 0, common: 1 }); +//// +////// Type cast +////var c = { common: 0, /*4*/b: 0 }; +//// +////// Array literal +////var ar: Array = [{ a: 0, common: "" }, { /*5*/b: 0, common: 0 }]; +//// +////// Nested object literal +////var ob: { aorb: A|B } = { aorb: { /*6*/b: 0, common: 0 } }; +//// +////// Widened type +////var w: A|B = { /*7*/b:undefined, common: undefined }; +//// +////// Untped -- should not be included +////var u1 = { a: 0, b: 0, common: "" }; +////var u2 = { b: 0, common: 0 }; + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.referencesCountIs(7); +}); diff --git a/tests/cases/fourslash/referencesForUnionProperties.ts b/tests/cases/fourslash/referencesForUnionProperties.ts new file mode 100644 index 0000000000000..f4ef85e8e4efb --- /dev/null +++ b/tests/cases/fourslash/referencesForUnionProperties.ts @@ -0,0 +1,35 @@ +/// + +////interface One { +//// common: { /*1*/a: number; }; +////} +//// +////interface Base { +//// /*2*/a: string; +//// b: string; +////} +//// +////interface HasAOrB extends Base { +//// /*3*/a: string; +//// b: string; +////} +//// +////interface Two { +//// common: HasAOrB; +////} +//// +////var x : One | Two; +//// +////x.common./*4*/a; + +goTo.marker("1"); +verify.referencesCountIs(2); // One.common.a, x.common.a + +goTo.marker("2"); +verify.referencesCountIs(3); // Base.a, HasAOrB.a, x.common.a + +goTo.marker("3"); +verify.referencesCountIs(3); // Base.a, HasAOrB.a, x.common.a + +goTo.marker("4"); +verify.referencesCountIs(4); // One.common.a, Base.a, HasAOrB.a, x.common.a \ No newline at end of file