Merge pull request #2729 from Microsoft/reducedUnionTypes

Consistently reduce union types
This commit is contained in:
Anders Hejlsberg 2015-04-14 15:24:49 -07:00
commit 6d36dd5296
7 changed files with 125 additions and 24 deletions

View File

@ -2904,16 +2904,17 @@ module ts {
}
function getPropertiesOfType(type: Type): Symbol[] {
if (type.flags & TypeFlags.Union) {
return getPropertiesOfUnionType(<UnionType>type);
}
return getPropertiesOfObjectType(getApparentType(type));
type = getApparentType(type);
return type.flags & TypeFlags.Union ? getPropertiesOfUnionType(<UnionType>type) : getPropertiesOfObjectType(type);
}
// For a type parameter, return the base constraint of the type parameter. For the string, number,
// boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
// type itself. Note that the apparent type of a union type is the union type itself.
function getApparentType(type: Type): Type {
if (type.flags & TypeFlags.Union) {
type = getReducedTypeOfUnionType(<UnionType>type);
}
if (type.flags & TypeFlags.TypeParameter) {
do {
type = getConstraintOfTypeParameter(<TypeParameter>type);
@ -2986,27 +2987,27 @@ module ts {
// necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
// Object and Function as appropriate.
function getPropertyOfType(type: Type, name: string): Symbol {
type = getApparentType(type);
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveObjectOrUnionTypeMembers(type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
let symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) {
return symbol;
}
}
return getPropertyOfObjectType(globalObjectType, name);
}
if (type.flags & TypeFlags.Union) {
return getPropertyOfUnionType(<UnionType>type, name);
}
if (!(type.flags & TypeFlags.ObjectType)) {
type = getApparentType(type);
if (!(type.flags & TypeFlags.ObjectType)) {
return undefined;
}
}
let resolved = resolveObjectOrUnionTypeMembers(type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
let symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) return symbol;
}
return getPropertyOfObjectType(globalObjectType, name);
return undefined;
}
function getSignaturesOfObjectOrUnionType(type: Type, kind: SignatureKind): Signature[] {
@ -3581,6 +3582,10 @@ module ts {
}
}
// The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag
// is true when creating a union type from a type node and when instantiating a union type. In both of those
// cases subtype reduction has to be deferred to properly support recursive union types. For example, a
// type alias of the form "type Item = string | (() => Item)" cannot be reduced during its declaration.
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
if (types.length === 0) {
return emptyObjectType;
@ -3605,10 +3610,19 @@ module ts {
if (!type) {
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(sortedTypes));
type.types = sortedTypes;
type.reducedType = noSubtypeReduction ? undefined : type;
}
return type;
}
function getReducedTypeOfUnionType(type: UnionType): Type {
// If union type was created without subtype reduction, perform the deferred reduction now
if (!type.reducedType) {
type.reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
}
return type.reducedType;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {

View File

@ -1512,6 +1512,8 @@ module ts {
export interface UnionType extends Type {
types: Type[]; // Constituent types
/* @internal */
reducedType: Type; // Reduced union type (all subtypes removed)
/* @internal */
resolvedProperties: SymbolTable; // Cache of resolved properties
}

View File

@ -0,0 +1,25 @@
//// [unionTypeReduction.ts]
interface I2 {
(): number;
(q): boolean;
}
interface I3 {
(): number;
}
var i2: I2, i3: I3;
var e1: I2 | I3;
var e2 = i2 || i3; // Type of e2 immediately reduced to I3
var r1 = e1(); // Type of e1 reduced to I3 upon accessing property or signature
var r2 = e2();
//// [unionTypeReduction.js]
var i2, i3;
var e1;
var e2 = i2 || i3; // Type of e2 immediately reduced to I3
var r1 = e1(); // Type of e1 reduced to I3 upon accessing property or signature
var r2 = e2();

View File

@ -0,0 +1,42 @@
=== tests/cases/conformance/types/union/unionTypeReduction.ts ===
interface I2 {
>I2 : I2, Symbol(I2, Decl(unionTypeReduction.ts, 0, 0))
(): number;
(q): boolean;
>q : any, Symbol(q, Decl(unionTypeReduction.ts, 2, 5))
}
interface I3 {
>I3 : I3, Symbol(I3, Decl(unionTypeReduction.ts, 3, 1))
(): number;
}
var i2: I2, i3: I3;
>i2 : I2, Symbol(i2, Decl(unionTypeReduction.ts, 9, 3))
>I2 : I2, Symbol(I2, Decl(unionTypeReduction.ts, 0, 0))
>i3 : I3, Symbol(i3, Decl(unionTypeReduction.ts, 9, 11))
>I3 : I3, Symbol(I3, Decl(unionTypeReduction.ts, 3, 1))
var e1: I2 | I3;
>e1 : I2 | I3, Symbol(e1, Decl(unionTypeReduction.ts, 11, 3))
>I2 : I2, Symbol(I2, Decl(unionTypeReduction.ts, 0, 0))
>I3 : I3, Symbol(I3, Decl(unionTypeReduction.ts, 3, 1))
var e2 = i2 || i3; // Type of e2 immediately reduced to I3
>e2 : I3, Symbol(e2, Decl(unionTypeReduction.ts, 12, 3))
>i2 || i3 : I3
>i2 : I2, Symbol(i2, Decl(unionTypeReduction.ts, 9, 3))
>i3 : I3, Symbol(i3, Decl(unionTypeReduction.ts, 9, 11))
var r1 = e1(); // Type of e1 reduced to I3 upon accessing property or signature
>r1 : number, Symbol(r1, Decl(unionTypeReduction.ts, 14, 3))
>e1() : number
>e1 : I2 | I3, Symbol(e1, Decl(unionTypeReduction.ts, 11, 3))
var r2 = e2();
>r2 : number, Symbol(r2, Decl(unionTypeReduction.ts, 15, 3))
>e2() : number
>e2 : I3, Symbol(e2, Decl(unionTypeReduction.ts, 12, 3))

View File

@ -0,0 +1,16 @@
interface I2 {
(): number;
(q): boolean;
}
interface I3 {
(): number;
}
var i2: I2, i3: I3;
var e1: I2 | I3;
var e2 = i2 || i3; // Type of e2 immediately reduced to I3
var r1 = e1(); // Type of e1 reduced to I3 upon accessing property or signature
var r2 = e2();

View File

@ -2,9 +2,11 @@
////module E {
//// export var n = 1;
//// export var x = 0;
////}
////module F {
//// export var n = 1;
//// export var y = 0;
////}
////var q: typeof E | typeof F;
////var j = q./*1*/

View File

@ -19,8 +19,8 @@
goTo.marker("propertyReference");
verify.definitionCountIs(2);
goTo.definition(0);
verify.caretAtMarker("propertyDefinition2");
verify.caretAtMarker("propertyDefinition1");
goTo.marker("propertyReference");
goTo.definition(1);
verify.caretAtMarker("propertyDefinition1");
verify.caretAtMarker("propertyDefinition2");