Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lanugage Service support for union types #861

Merged
merged 12 commits into from
Oct 11, 2014
31 changes: 27 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ module ts {
symbolToString: symbolToString,
symbolToDisplayParts: symbolToDisplayParts,
getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType,
getRootSymbol: getRootSymbol,
getRootSymbols: getRootSymbols,
getContextualType: getContextualType,
getFullyQualifiedName: getFullyQualifiedName,
getResolvedSignature: getResolvedSignature,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
219 changes: 128 additions & 91 deletions src/services/services.ts

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions tests/cases/fourslash/completionEntryForUnionProperty.ts
Original file line number Diff line number Diff line change
@@ -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);
19 changes: 19 additions & 0 deletions tests/cases/fourslash/completionEntryForUnionProperty2.ts
Original file line number Diff line number Diff line change
@@ -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);
24 changes: 24 additions & 0 deletions tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts
Original file line number Diff line number Diff line change
@@ -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");
25 changes: 25 additions & 0 deletions tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts
Original file line number Diff line number Diff line change
@@ -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");
27 changes: 27 additions & 0 deletions tests/cases/fourslash/quickinfoForUnionProperty.ts
Original file line number Diff line number Diff line change
@@ -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");
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// <reference path='fourslash.ts'/>

/// <reference path='fourslash.ts'/>
////interface IFoo { /*1*/xy: number; }
////
////// Assignment
Expand All @@ -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);
});
Original file line number Diff line number Diff line change
@@ -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)
});
Original file line number Diff line number Diff line change
@@ -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);
});
35 changes: 35 additions & 0 deletions tests/cases/fourslash/referencesForUnionProperties.ts
Original file line number Diff line number Diff line change
@@ -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