diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3201434da5839..369f955c06d88 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -88,6 +88,7 @@ module ts { let undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined"); let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null"); let unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); + let circularType = createIntrinsicType(TypeFlags.Any, "__circular__"); let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); @@ -3566,19 +3567,7 @@ module ts { return false; } - // Since removeSubtypes checks the subtype relation, and the subtype relation on a union - // may attempt to reduce a union, it is possible that removeSubtypes could be called - // recursively on the same set of types. The removeSubtypesStack is used to track which - // sets of types are currently undergoing subtype reduction. - let removeSubtypesStack: string[] = []; function removeSubtypes(types: Type[]) { - let typeListId = getTypeListId(types); - if (removeSubtypesStack.lastIndexOf(typeListId) >= 0) { - return; - } - - removeSubtypesStack.push(typeListId); - let i = types.length; while (i > 0) { i--; @@ -3586,8 +3575,6 @@ module ts { types.splice(i, 1); } } - - removeSubtypesStack.pop(); } function containsAnyType(types: Type[]) { @@ -3642,10 +3629,20 @@ module ts { return type; } + // Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer + // to process and look strange in quick info and error messages. Semantically there is no difference between the + // reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the + // type itself. 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); + type.reducedType = circularType; + let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false); + if (type.reducedType === circularType) { + type.reducedType = reducedType; + } + } + else if (type.reducedType === circularType) { + type.reducedType = type; } return type.reducedType; } diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.js similarity index 82% rename from tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js rename to tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.js index 9442d733aa475..f8a5cd96b4858 100644 --- a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.js @@ -1,4 +1,4 @@ -//// [unionTypeWithRecursiveSubtypeReduction.ts] +//// [unionTypeWithRecursiveSubtypeReduction1.ts] class Module { public members: Class[]; } @@ -16,9 +16,10 @@ class Property { } var t: Class | Property; -t.parent; +t.parent; + -//// [unionTypeWithRecursiveSubtypeReduction.js] +//// [unionTypeWithRecursiveSubtypeReduction1.js] var Module = (function () { function Module() { } diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.symbols similarity index 66% rename from tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols rename to tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.symbols index 93351990f6c27..24ba3b8a8cda4 100644 --- a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.symbols @@ -1,45 +1,45 @@ -=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts === +=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts === class Module { ->Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0)) +>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 0)) public members: Class[]; ->members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 14)) ->Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 14)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1)) } class Namespace { ->Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1)) +>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 2, 1)) public members: (Class | Property)[]; ->members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 4, 17)) ->Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) ->Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) +>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 4, 17)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1)) +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1)) } class Class { ->Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1)) public parent: Namespace; ->parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13)) ->Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1)) +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13)) +>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 2, 1)) } class Property { ->Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1)) public parent: Module | Class; ->parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) ->Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0)) ->Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16)) +>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 0)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1)) } var t: Class | Property; ->t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3)) ->Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) ->Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) +>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 16, 3)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1)) +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1)) t.parent; ->t.parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) ->t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3)) ->parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) +>t.parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16)) +>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 16, 3)) +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16)) diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.types similarity index 85% rename from tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types rename to tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.types index cf8f41d70ccc7..c6fa610e68ae7 100644 --- a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.types @@ -1,4 +1,4 @@ -=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts === +=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts === class Module { >Module : Module diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.errors.txt b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.errors.txt index 0f15bd1ed3a63..d481b88810caf 100644 --- a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.errors.txt +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.errors.txt @@ -42,4 +42,5 @@ tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts(20,1): error TS2 !!! error TS2322: Types of property 'parent' are incompatible. !!! error TS2322: Type 'Namespace' is not assignable to type 'Module | Class'. !!! error TS2322: Type 'Namespace' is not assignable to type 'Class'. -!!! error TS2322: Property 'parent' is missing in type 'Namespace'. \ No newline at end of file +!!! error TS2322: Property 'parent' is missing in type 'Namespace'. + \ No newline at end of file diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.js b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.js index 6b3c61dbeed12..69411b1ffcb21 100644 --- a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.js +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.js @@ -18,7 +18,8 @@ class Property { var c: Class; var p: Property; c = p; -p = c; +p = c; + //// [unionTypeWithRecursiveSubtypeReduction2.js] var Module = (function () { diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.errors.txt b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.errors.txt new file mode 100644 index 0000000000000..279119e679acf --- /dev/null +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts(5,5): error TS2322: Type '{ prop: number; } | { prop: { prop: number; } | any; }' is not assignable to type 'string'. + Type '{ prop: number; }' is not assignable to type 'string'. + + +==== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts (1 errors) ==== + var a27: { prop: number } | { prop: T27 }; + type T27 = typeof a27; + + var b: T27; + var s: string = b; + ~ +!!! error TS2322: Type '{ prop: number; } | { prop: { prop: number; } | any; }' is not assignable to type 'string'. +!!! error TS2322: Type '{ prop: number; }' is not assignable to type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.js b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.js new file mode 100644 index 0000000000000..7c33601505909 --- /dev/null +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction3.js @@ -0,0 +1,12 @@ +//// [unionTypeWithRecursiveSubtypeReduction3.ts] +var a27: { prop: number } | { prop: T27 }; +type T27 = typeof a27; + +var b: T27; +var s: string = b; + + +//// [unionTypeWithRecursiveSubtypeReduction3.js] +var a27; +var b; +var s = b; diff --git a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts similarity index 89% rename from tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts rename to tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts index 651adf44895ea..acdb91c99352b 100644 --- a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts +++ b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts @@ -15,4 +15,4 @@ class Property { } var t: Class | Property; -t.parent; \ No newline at end of file +t.parent; diff --git a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts index 31a8bbe0b6752..cddcf4d45664d 100644 --- a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts +++ b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts @@ -17,4 +17,4 @@ class Property { var c: Class; var p: Property; c = p; -p = c; \ No newline at end of file +p = c; diff --git a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts new file mode 100644 index 0000000000000..4bac9ca1b9c83 --- /dev/null +++ b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts @@ -0,0 +1,5 @@ +var a27: { prop: number } | { prop: T27 }; +type T27 = typeof a27; + +var b: T27; +var s: string = b;