Skip to content

Commit

Permalink
Merge pull request #861 from Microsoft/unionTypesLS
Browse files Browse the repository at this point in the history
Lanugage Service support for union types
  • Loading branch information
mhegazy committed Oct 11, 2014
2 parents c9a42c1 + 04e5309 commit eee1602
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 105 deletions.
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

0 comments on commit eee1602

Please sign in to comment.