Merge pull request #861 from Microsoft/unionTypesLS

Lanugage Service support for union types
This commit is contained in:
Mohamed Hegazy 2014-10-10 18:11:49 -07:00
commit eee1602b70
12 changed files with 396 additions and 106 deletions

View File

@ -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 = <TransientSymbol>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

View File

@ -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;

View File

@ -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(<EnumMember>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(<SourceFile>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;
}
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>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;
}
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 = (<Identifier>node).text;
if (contextualType) {
return typeInfoResolver.getPropertyOfType(contextualType, (<Identifier>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((<UnionType>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;

View File

@ -0,0 +1,20 @@
///<reference path="fourslash.ts" />
////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);

View File

@ -0,0 +1,19 @@
///<reference path="fourslash.ts" />
////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);

View File

@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />
////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");

View File

@ -0,0 +1,25 @@
/// <reference path='fourslash.ts' />
////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");

View File

@ -0,0 +1,27 @@
/// <reference path="fourslash.ts"/>
////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");

View File

@ -1,5 +1,5 @@
/// <reference path='fourslash.ts'/>
/// <reference path='fourslash.ts'/>
////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);
});

View File

@ -0,0 +1,40 @@
/// <reference path='fourslash.ts'/>
////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 = <A | B> { /*4*/common: 0, b: 0 };
////
////// Array literal
////var ar: Array<A|B> = [{ 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)
});

View File

@ -0,0 +1,40 @@
/// <reference path='fourslash.ts'/>
////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 = <A | B> { common: 0, /*4*/b: 0 };
////
////// Array literal
////var ar: Array<A|B> = [{ 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);
});

View File

@ -0,0 +1,35 @@
/// <reference path='fourslash.ts'/>
////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