From 419e8d6eb56f1a14b912118baba61ef009f9341e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 7 Aug 2020 20:26:24 -0700 Subject: [PATCH 01/12] Support recursive conditional types --- src/compiler/checker.ts | 154 ++++++++++++++++++---------------------- src/compiler/types.ts | 2 - 2 files changed, 68 insertions(+), 88 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 748cab985dab1..596b4f649d55c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13809,11 +13809,11 @@ namespace ts { if (!(inferredExtendsType.flags & TypeFlags.AnyOrUnknown) && (checkType.flags & TypeFlags.Any || !isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType)))) { // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { - (extraTypes || (extraTypes = [])).push(instantiateTypeWithoutDepthIncrease(root.trueType, combinedMapper || mapper)); + (extraTypes || (extraTypes = [])).push(instantiateType(getTypeFromTypeNode(root.node.trueType), combinedMapper || mapper)); } // If falseType is an immediately nested conditional type that isn't distributive or has an // identical checkType, switch to that type and loop. - const falseType = root.falseType; + const falseType = getTypeFromTypeNode(root.node.falseType); if (falseType.flags & TypeFlags.Conditional) { const newRoot = (falseType).root; if (newRoot.node.parent === root.node && (!newRoot.isDistributive || newRoot.checkType === root.checkType)) { @@ -13821,7 +13821,7 @@ namespace ts { continue; } } - result = instantiateTypeWithoutDepthIncrease(falseType, mapper); + result = instantiateType(falseType, mapper); break; } // Return trueType for a definitely true extends check. We check instantiations of the two @@ -13830,7 +13830,7 @@ namespace ts { // type Foo = T extends { x: string } ? string : number // doesn't immediately resolve to 'string' instead of being deferred. if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { - result = instantiateTypeWithoutDepthIncrease(root.trueType, combinedMapper || mapper); + result = instantiateType(getTypeFromTypeNode(root.node.trueType), combinedMapper || mapper); break; } } @@ -13850,15 +13850,15 @@ namespace ts { } function getTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(type.root.trueType, type.mapper)); + return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(getTypeFromTypeNode(type.root.node.trueType), type.mapper)); } function getFalseTypeFromConditionalType(type: ConditionalType) { - return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper)); + return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(getTypeFromTypeNode(type.root.node.falseType), type.mapper)); } function getInferredTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedInferredTrueType || (type.resolvedInferredTrueType = type.combinedMapper ? instantiateType(type.root.trueType, type.combinedMapper) : getTrueTypeFromConditionalType(type)); + return type.resolvedInferredTrueType || (type.resolvedInferredTrueType = type.combinedMapper ? instantiateType(getTypeFromTypeNode(type.root.node.trueType), type.combinedMapper) : getTrueTypeFromConditionalType(type)); } function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] | undefined { @@ -13885,8 +13885,6 @@ namespace ts { node, checkType, extendsType: getTypeFromTypeNode(node.extendsType), - trueType: getTypeFromTypeNode(node.trueType), - falseType: getTypeFromTypeNode(node.falseType), isDistributive: !!(checkType.flags & TypeFlags.TypeParameter), inferTypeParameters: getInferTypeParameters(node), outerTypeParameters, @@ -14877,17 +14875,6 @@ namespace ts { return result; } - /** - * This can be used to avoid the penalty on instantiation depth for types which result from immediate - * simplification. It essentially removes the depth increase done in `instantiateType`. - */ - function instantiateTypeWithoutDepthIncrease(type: Type, mapper: TypeMapper | undefined) { - instantiationDepth--; - const result = instantiateType(type, mapper); - instantiationDepth++; - return result; - } - function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type { const flags = type.flags; if (flags & TypeFlags.TypeParameter) { @@ -18199,56 +18186,44 @@ namespace ts { // has expanded into `[A>>>>>]` // in such cases we need to terminate the expansion, and we do so here. function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean { - // We track all object types that have an associated symbol (representing the origin of the type) - if (depth >= 5 && type.flags & TypeFlags.Object) { - if (!isObjectOrArrayLiteralType(type)) { - const symbol = type.symbol; - if (symbol) { - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (t.flags & TypeFlags.Object && t.symbol === symbol) { - count++; - if (count >= 5) return true; - } - } - } - } - if (getObjectFlags(type) && ObjectFlags.Reference && !!(type as TypeReference).node) { - const root = (type as TypeReference).target; + if (depth >= 5) { + const identity = getRecursionIdentity(type); + if (identity) { let count = 0; for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getObjectFlags(t) && ObjectFlags.Reference && !!(t as TypeReference).node && (t as TypeReference).target === root) { + if (getRecursionIdentity(stack[i]) === identity) { count++; if (count >= 5) return true; } } } } - if (depth >= 5 && type.flags & TypeFlags.IndexedAccess) { - const root = getRootObjectTypeFromIndexedAccessChain(type); - let count = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getRootObjectTypeFromIndexedAccessChain(t) === root) { - count++; - if (count >= 5) return true; - } - } - } return false; } - /** - * Gets the leftmost object type in a chain of indexed accesses, eg, in A[P][Q], returns A - */ - function getRootObjectTypeFromIndexedAccessChain(type: Type) { - let t = type; - while (t.flags & TypeFlags.IndexedAccess) { - t = (t as IndexedAccessType).objectType; + function getRecursionIdentity(type: Type) { + if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) { + if (type.symbol) { + // We track all object types that have an associated symbol (representing the origin of the type) + return type.symbol; + } + if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node || isTupleType(type)) { + // Deferred type references and tuple types are tracked through their target type + return (type as TypeReference).target; + } } - return t; + if (type.flags & TypeFlags.IndexedAccess) { + // Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P][Q] it is A + do { + type = (type as IndexedAccessType).objectType; + } while (type.flags & TypeFlags.IndexedAccess); + return type; + } + if (type.flags & TypeFlags.Conditional) { + // The root object represents the origin of the conditional type + return (type as ConditionalType).root; + } + return undefined; } function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean { @@ -19153,9 +19128,7 @@ namespace ts { function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { return !!(type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && some((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)) || - type.flags & TypeFlags.Conditional && ( - isTypeParameterAtTopLevel(getTrueTypeFromConditionalType(type), typeParameter) || - isTypeParameterAtTopLevel(getFalseTypeFromConditionalType(type), typeParameter))); + type.flags & TypeFlags.Conditional && (getTrueTypeFromConditionalType(type) === typeParameter || getFalseTypeFromConditionalType(type) === typeParameter)); } /** Create an object with properties named in the string literal type. Every property has type `any` */ @@ -19311,7 +19284,7 @@ namespace ts { let propagationType: Type; let inferencePriority = InferencePriority.MaxValue; let allowComplexConstraintInference = true; - let objectTypeComparisonDepth = 0; + let targetStackDepth = 0; const targetStack: Type[] = []; inferFromTypes(originalSource, originalTarget); @@ -19471,18 +19444,8 @@ namespace ts { inferFromTypes((source).objectType, (target).objectType); inferFromTypes((source).indexType, (target).indexType); } - else if (source.flags & TypeFlags.Conditional && target.flags & TypeFlags.Conditional) { - inferFromTypes((source).checkType, (target).checkType); - inferFromTypes((source).extendsType, (target).extendsType); - inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target)); - inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); - } else if (target.flags & TypeFlags.Conditional) { - const savePriority = priority; - priority |= contravariant ? InferencePriority.ContravariantConditional : 0; - const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)]; - inferToMultipleTypes(source, targetTypes, target.flags); - priority = savePriority; + invokeWithDepthLimit(source, target, inferToConditionalType); } else if (target.flags & TypeFlags.UnionOrIntersection) { inferToMultipleTypes(source, (target).types, target.flags); @@ -19739,6 +19702,22 @@ namespace ts { return false; } + function inferToConditionalType(source: ConditionalType, target: ConditionalType) { + if (source.flags & TypeFlags.Conditional) { + inferFromTypes(source.checkType, target.checkType); + inferFromTypes(source.extendsType, target.extendsType); + inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target)); + inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); + } + else { + const savePriority = priority; + priority |= contravariant ? InferencePriority.ContravariantConditional : 0; + const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)]; + inferToMultipleTypes(source, targetTypes, target.flags); + priority = savePriority; + } + } + function inferFromObjectTypes(source: Type, target: Type) { // If we are already processing another target type with the same associated symbol (such as // an instantiation of the same generic type), we do not explore this target as it would yield @@ -19749,30 +19728,33 @@ namespace ts { const symbolOrType = getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node ? getNormalizedType(target, /*writing*/ false) : isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined; if (symbolOrType) { if (contains(symbolOrTypeStack, symbolOrType)) { - if (getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node) { - // Don't set the circularity flag for re-encountered recursive type references just because we're already exploring them - return; + // Don't set the circularity flag for re-encountered recursive type references just because we're already exploring them + if (!(getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node)) { + inferencePriority = InferencePriority.Circularity; } - inferencePriority = InferencePriority.Circularity; - return; - } - targetStack[objectTypeComparisonDepth] = target; - objectTypeComparisonDepth++; - if (isDeeplyNestedType(target, targetStack, objectTypeComparisonDepth)) { - inferencePriority = InferencePriority.Circularity; - objectTypeComparisonDepth--; return; } (symbolOrTypeStack || (symbolOrTypeStack = [])).push(symbolOrType); - inferFromObjectTypesWorker(source, target); + invokeWithDepthLimit(source, target, inferFromObjectTypesWorker); symbolOrTypeStack.pop(); - objectTypeComparisonDepth--; } else { inferFromObjectTypesWorker(source, target); } } + function invokeWithDepthLimit(source: Type, target: Type, action: (source: Type, target: Type) => void) { + targetStack[targetStackDepth] = target; + targetStackDepth++; + if (!isDeeplyNestedType(target, targetStack, targetStackDepth)) { + action(source, target); + } + else { + inferencePriority = InferencePriority.Circularity; + } + targetStackDepth--; + } + function inferFromObjectTypesWorker(source: Type, target: Type) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( (source).target === (target).target || isArrayType(source) && isArrayType(target))) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d9468bd51234e..02b06de53a334 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5292,8 +5292,6 @@ namespace ts { node: ConditionalTypeNode; checkType: Type; extendsType: Type; - trueType: Type; - falseType: Type; isDistributive: boolean; inferTypeParameters?: TypeParameter[]; outerTypeParameters?: TypeParameter[]; From beab30aeaf7d12b5a32af1e4d2b26769d13c1c58 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 7 Aug 2020 20:32:01 -0700 Subject: [PATCH 02/12] Accept new API baselines --- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 -- tests/baselines/reference/api/typescript.d.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index db58c35ac46e0..742c4f915ea81 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2605,8 +2605,6 @@ declare namespace ts { node: ConditionalTypeNode; checkType: Type; extendsType: Type; - trueType: Type; - falseType: Type; isDistributive: boolean; inferTypeParameters?: TypeParameter[]; outerTypeParameters?: TypeParameter[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index a0dab441a385f..b8af3d64d89f1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2605,8 +2605,6 @@ declare namespace ts { node: ConditionalTypeNode; checkType: Type; extendsType: Type; - trueType: Type; - falseType: Type; isDistributive: boolean; inferTypeParameters?: TypeParameter[]; outerTypeParameters?: TypeParameter[]; From c24cacce4257e1302219cb9e71185c9ce49c24a2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 7 Aug 2020 20:32:17 -0700 Subject: [PATCH 03/12] Accept new baselines --- tests/baselines/reference/deeplyNestedConditionalTypes.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/deeplyNestedConditionalTypes.types b/tests/baselines/reference/deeplyNestedConditionalTypes.types index d233e86bbbb86..ef0511f7f3fea 100644 --- a/tests/baselines/reference/deeplyNestedConditionalTypes.types +++ b/tests/baselines/reference/deeplyNestedConditionalTypes.types @@ -108,5 +108,5 @@ type T0 = Foo<99>; >T0 : "99" type T1 = Foo; ->T1 : "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" | "43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52" | "53" | "54" | "55" | "56" | "57" | "58" | "59" | "60" | "61" | "62" | "63" | "64" | "65" | "66" | "67" | "68" | "69" | "70" | "71" | "72" | "73" | "74" | "75" | "76" | "77" | "78" | "79" | "80" | "81" | "82" | "83" | "84" | "85" | "86" | "87" | "88" | "89" | "90" | "91" | "92" | "93" | "94" | "95" | "96" | "97" | "98" | "99" +>T1 : "99" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" | "43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52" | "53" | "54" | "55" | "56" | "57" | "58" | "59" | "60" | "61" | "62" | "63" | "64" | "65" | "66" | "67" | "68" | "69" | "70" | "71" | "72" | "73" | "74" | "75" | "76" | "77" | "78" | "79" | "80" | "81" | "82" | "83" | "84" | "85" | "86" | "87" | "88" | "89" | "90" | "91" | "92" | "93" | "94" | "95" | "96" | "97" | "98" From 5a45585b99cad062262f0d793573ed8b156ab1aa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 10 Aug 2020 10:43:57 -0700 Subject: [PATCH 04/12] Simplify recursive type tracking in type inference --- src/compiler/checker.ts | 63 +++++++++++------------------------------ 1 file changed, 17 insertions(+), 46 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 596b4f649d55c..9456e4eb4c191 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19278,14 +19278,12 @@ namespace ts { } function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { - let symbolOrTypeStack: (Symbol | Type)[]; - let visited: ESMap; let bivariant = false; let propagationType: Type; let inferencePriority = InferencePriority.MaxValue; let allowComplexConstraintInference = true; - let targetStackDepth = 0; - const targetStack: Type[] = []; + let visited: ESMap; + let targetStack: Type[]; inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type): void { @@ -19408,7 +19406,7 @@ namespace ts { // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine const simplified = getSimplifiedType(target, /*writing*/ false); if (simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); + invokeWithDepthLimit(source, simplified, inferFromTypes); } else if (target.flags & TypeFlags.IndexedAccess) { const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); @@ -19417,7 +19415,7 @@ namespace ts { if (indexType.flags & TypeFlags.Instantiable) { const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); if (simplified && simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); + invokeWithDepthLimit(source, simplified, inferFromTypes); } } } @@ -19478,7 +19476,7 @@ namespace ts { source = apparentSource; } if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) { - invokeOnce(source, target, inferFromObjectTypes); + invokeWithDepthLimit(source, target, inferFromObjectTypes); } } if (source.flags & TypeFlags.Simplifiable) { @@ -19496,7 +19494,7 @@ namespace ts { priority = savePriority; } - function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) { + function invokeWithDepthLimit(source: Type, target: Type, action: (source: Type, target: Type) => void) { const key = source.id + "," + target.id; const status = visited && visited.get(key); if (status !== undefined) { @@ -19506,7 +19504,17 @@ namespace ts { (visited || (visited = new Map())).set(key, InferencePriority.Circularity); const saveInferencePriority = inferencePriority; inferencePriority = InferencePriority.MaxValue; - action(source, target); + // It is possible for recursion to originate in generative types that create infinitely deep instantiations, + // with unique identities, for example 'type RecArray = T | Array>'. We explore up to five + // nested instantiations of such types using the same isDeeplyNestedType check as recursiveTypeRelatedTo. + (targetStack || (targetStack = [])).push(target); + if (!isDeeplyNestedType(target, targetStack, targetStack.length)) { + action(source, target); + } + else { + inferencePriority = InferencePriority.Circularity; + } + targetStack.pop(); visited.set(key, inferencePriority); inferencePriority = Math.min(inferencePriority, saveInferencePriority); } @@ -19719,43 +19727,6 @@ namespace ts { } function inferFromObjectTypes(source: Type, target: Type) { - // If we are already processing another target type with the same associated symbol (such as - // an instantiation of the same generic type), we do not explore this target as it would yield - // no further inferences. We exclude the static side of classes from this check since it shares - // its symbol with the instance side which would lead to false positives. - const isNonConstructorObject = target.flags & TypeFlags.Object && - !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class); - const symbolOrType = getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node ? getNormalizedType(target, /*writing*/ false) : isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined; - if (symbolOrType) { - if (contains(symbolOrTypeStack, symbolOrType)) { - // Don't set the circularity flag for re-encountered recursive type references just because we're already exploring them - if (!(getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node)) { - inferencePriority = InferencePriority.Circularity; - } - return; - } - (symbolOrTypeStack || (symbolOrTypeStack = [])).push(symbolOrType); - invokeWithDepthLimit(source, target, inferFromObjectTypesWorker); - symbolOrTypeStack.pop(); - } - else { - inferFromObjectTypesWorker(source, target); - } - } - - function invokeWithDepthLimit(source: Type, target: Type, action: (source: Type, target: Type) => void) { - targetStack[targetStackDepth] = target; - targetStackDepth++; - if (!isDeeplyNestedType(target, targetStack, targetStackDepth)) { - action(source, target); - } - else { - inferencePriority = InferencePriority.Circularity; - } - targetStackDepth--; - } - - function inferFromObjectTypesWorker(source: Type, target: Type) { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( (source).target === (target).target || isArrayType(source) && isArrayType(target))) { // If source and target are references to the same generic type, infer from type arguments From 4f8dc24e036358d72b94b3da3ac2117db0770157 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 10 Aug 2020 10:44:19 -0700 Subject: [PATCH 05/12] Accept new baselines --- tests/baselines/reference/promiseTypeInference.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/promiseTypeInference.types b/tests/baselines/reference/promiseTypeInference.types index 2484e9b97d207..d1d2072e91c7a 100644 --- a/tests/baselines/reference/promiseTypeInference.types +++ b/tests/baselines/reference/promiseTypeInference.types @@ -22,8 +22,8 @@ declare function convert(s: string): IPromise; >s : string var $$x = load("something").then(s => convert(s)); ->$$x : CPromise ->load("something").then(s => convert(s)) : CPromise +>$$x : CPromise +>load("something").then(s => convert(s)) : CPromise >load("something").then : (success?: (value: string) => CPromise) => CPromise >load("something") : CPromise >load : (name: string) => CPromise From dd64bf88a792c2be2fc41d1468734fd6697953b5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 10 Aug 2020 19:56:18 -0700 Subject: [PATCH 06/12] Add tests --- .../compiler/recursiveConditionalTypes.ts | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/cases/compiler/recursiveConditionalTypes.ts diff --git a/tests/cases/compiler/recursiveConditionalTypes.ts b/tests/cases/compiler/recursiveConditionalTypes.ts new file mode 100644 index 0000000000000..33f7e63c47791 --- /dev/null +++ b/tests/cases/compiler/recursiveConditionalTypes.ts @@ -0,0 +1,93 @@ +// @strict: true +// @declaration: true + +// Awaiting promises + +type Awaited = + T extends null | undefined ? T : + T extends PromiseLike ? Awaited : + T; + +type MyPromise = { + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; +} + +type InfinitePromise = Promise>; + +type P0 = Awaited | null> | undefined>>; +type P1 = Awaited; +type P2 = Awaited>; // Error + +function f11(tx: T, ta: Awaited, ux: U, ua: Awaited) { + ta = ua; + ua = ta; // Error + ta = tx; // Error + tx = ta; // Error +} + +// Flattening arrays + +type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; +type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; + +type InfiniteArray = InfiniteArray[]; + +type B0 = Flatten; +type B1 = Flatten; +type B2 = Flatten>; +type B3 = B2[0]; // Error + +// Repeating tuples + +type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; +type _TupleOf = R['length'] extends N ? R : _TupleOf; + +type TT0 = TupleOf; +type TT1 = TupleOf; +type TT2 = TupleOf; +type TT3 = TupleOf; +type TT4 = TupleOf; // Depth error + +function f22(tn: TupleOf, tm: TupleOf) { + tn = tm; + tm = tn; +} + +declare function f23(t: TupleOf): T; + +f23(['a', 'b', 'c']); // string + +// Inference from nested instantiations of same generic types + +type Box1 = { value: T }; +type Box2 = { value: T }; + +declare function foo(x: Box1>): T; + +declare let z: Box2>; + +foo(z); // string + +// Intersect tuple element types + +type Intersect = U extends [infer H, ...infer T] ? Intersect : R; + +type QQ = Intersect<[string[], number[], 7]>; + +// Infer between structurally identical recursive conditional types + +type Unpack1 = T extends (infer U)[] ? Unpack1 : T; +type Unpack2 = T extends (infer U)[] ? Unpack2 : T; + +function f20(x: Unpack1, y: Unpack2) { + x = y; + y = x; + f20(y, x); +} + +type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; +type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; + +function f21(x: Grow1<[], T>, y: Grow2<[], T>) { + f21(y, x); // Error +} From 7c4d923a42814c54d07cf9c4a6c1feb8b0375b61 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 10 Aug 2020 19:56:27 -0700 Subject: [PATCH 07/12] Accept new baselines --- .../recursiveConditionalTypes.errors.txt | 168 +++++++++ .../reference/recursiveConditionalTypes.js | 160 ++++++++ .../recursiveConditionalTypes.symbols | 352 ++++++++++++++++++ .../reference/recursiveConditionalTypes.types | 215 +++++++++++ 4 files changed, 895 insertions(+) create mode 100644 tests/baselines/reference/recursiveConditionalTypes.errors.txt create mode 100644 tests/baselines/reference/recursiveConditionalTypes.js create mode 100644 tests/baselines/reference/recursiveConditionalTypes.symbols create mode 100644 tests/baselines/reference/recursiveConditionalTypes.types diff --git a/tests/baselines/reference/recursiveConditionalTypes.errors.txt b/tests/baselines/reference/recursiveConditionalTypes.errors.txt new file mode 100644 index 0000000000000..2b084c3554cc2 --- /dev/null +++ b/tests/baselines/reference/recursiveConditionalTypes.errors.txt @@ -0,0 +1,168 @@ +tests/cases/compiler/recursiveConditionalTypes.ts(16,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/recursiveConditionalTypes.ts(20,5): error TS2322: Type 'Awaited' is not assignable to type 'Awaited'. + Type 'T' is not assignable to type 'U'. + 'U' could be instantiated with an arbitrary type which could be unrelated to 'T'. +tests/cases/compiler/recursiveConditionalTypes.ts(21,5): error TS2322: Type 'T' is not assignable to type 'Awaited'. +tests/cases/compiler/recursiveConditionalTypes.ts(22,5): error TS2322: Type 'Awaited' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited'. + Type 'T | (T extends PromiseLike ? Awaited : T)' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends PromiseLike ? Awaited : T)'. + Type 'T extends PromiseLike ? Awaited : T' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'T extends PromiseLike ? Awaited : T'. + Type 'unknown' is not assignable to type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'. +tests/cases/compiler/recursiveConditionalTypes.ts(35,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/recursiveConditionalTypes.ts(46,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/recursiveConditionalTypes.ts(49,5): error TS2322: Type 'TupleOf' is not assignable to type 'TupleOf'. + Type 'number extends M ? number[] : _TupleOf' is not assignable to type 'TupleOf'. + Type 'number[] | _TupleOf' is not assignable to type 'TupleOf'. + Type 'number[]' is not assignable to type 'TupleOf'. +tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'TupleOf' is not assignable to type 'TupleOf'. + Type 'number extends N ? number[] : _TupleOf' is not assignable to type 'TupleOf'. + Type 'number[] | _TupleOf' is not assignable to type 'TupleOf'. + Type 'number[]' is not assignable to type 'TupleOf'. +tests/cases/compiler/recursiveConditionalTypes.ts(89,5): error TS2589: Type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/recursiveConditionalTypes.ts(89,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'. + Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'. + Type '[]' is not assignable to type 'Grow1<[], T>'. + Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'. + Type '[string] | Grow2<[string, string], T>' is not assignable to type 'Grow1<[number], T>'. + Type '[string]' is not assignable to type 'Grow1<[number], T>'. + Type '[string]' is not assignable to type '[number]'. + Type 'string' is not assignable to type 'number'. + + +==== tests/cases/compiler/recursiveConditionalTypes.ts (10 errors) ==== + // Awaiting promises + + type Awaited = + T extends null | undefined ? T : + T extends PromiseLike ? Awaited : + T; + + type MyPromise = { + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + } + + type InfinitePromise = Promise>; + + type P0 = Awaited | null> | undefined>>; + type P1 = Awaited; + type P2 = Awaited>; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + + function f11(tx: T, ta: Awaited, ux: U, ua: Awaited) { + ta = ua; + ua = ta; // Error + ~~ +!!! error TS2322: Type 'Awaited' is not assignable to type 'Awaited'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'T'. + ta = tx; // Error + ~~ +!!! error TS2322: Type 'T' is not assignable to type 'Awaited'. + tx = ta; // Error + ~~ +!!! error TS2322: Type 'Awaited' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited'. +!!! error TS2322: Type 'T | (T extends PromiseLike ? Awaited : T)' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends PromiseLike ? Awaited : T)'. +!!! error TS2322: Type 'T extends PromiseLike ? Awaited : T' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T extends PromiseLike ? Awaited : T'. +!!! error TS2322: Type 'unknown' is not assignable to type 'T'. +!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'. + } + + // Flattening arrays + + type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; + type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; + + type InfiniteArray = InfiniteArray[]; + + type B0 = Flatten; + type B1 = Flatten; + type B2 = Flatten>; + type B3 = B2[0]; // Error + ~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + + // Repeating tuples + + type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; + type _TupleOf = R['length'] extends N ? R : _TupleOf; + + type TT0 = TupleOf; + type TT1 = TupleOf; + type TT2 = TupleOf; + type TT3 = TupleOf; + type TT4 = TupleOf; // Depth error + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + + function f22(tn: TupleOf, tm: TupleOf) { + tn = tm; + ~~ +!!! error TS2322: Type 'TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number extends M ? number[] : _TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number[] | _TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf'. + tm = tn; + ~~ +!!! error TS2322: Type 'TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number extends N ? number[] : _TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number[] | _TupleOf' is not assignable to type 'TupleOf'. +!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf'. + } + + declare function f23(t: TupleOf): T; + + f23(['a', 'b', 'c']); // string + + // Inference from nested instantiations of same generic types + + type Box1 = { value: T }; + type Box2 = { value: T }; + + declare function foo(x: Box1>): T; + + declare let z: Box2>; + + foo(z); // string + + // Intersect tuple element types + + type Intersect = U extends [infer H, ...infer T] ? Intersect : R; + + type QQ = Intersect<[string[], number[], 7]>; + + // Infer between structurally identical recursive conditional types + + type Unpack1 = T extends (infer U)[] ? Unpack1 : T; + type Unpack2 = T extends (infer U)[] ? Unpack2 : T; + + function f20(x: Unpack1, y: Unpack2) { + x = y; + y = x; + f20(y, x); + } + + type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; + type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; + + function f21(x: Grow1<[], T>, y: Grow2<[], T>) { + f21(y, x); // Error + ~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~ +!!! error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'. +!!! error TS2345: Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'. +!!! error TS2345: Type '[]' is not assignable to type 'Grow1<[], T>'. +!!! error TS2345: Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'. +!!! error TS2345: Type '[string] | Grow2<[string, string], T>' is not assignable to type 'Grow1<[number], T>'. +!!! error TS2345: Type '[string]' is not assignable to type 'Grow1<[number], T>'. +!!! error TS2345: Type '[string]' is not assignable to type '[number]'. +!!! error TS2345: Type 'string' is not assignable to type 'number'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/recursiveConditionalTypes.js b/tests/baselines/reference/recursiveConditionalTypes.js new file mode 100644 index 0000000000000..4d96f319b3e44 --- /dev/null +++ b/tests/baselines/reference/recursiveConditionalTypes.js @@ -0,0 +1,160 @@ +//// [recursiveConditionalTypes.ts] +// Awaiting promises + +type Awaited = + T extends null | undefined ? T : + T extends PromiseLike ? Awaited : + T; + +type MyPromise = { + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; +} + +type InfinitePromise = Promise>; + +type P0 = Awaited | null> | undefined>>; +type P1 = Awaited; +type P2 = Awaited>; // Error + +function f11(tx: T, ta: Awaited, ux: U, ua: Awaited) { + ta = ua; + ua = ta; // Error + ta = tx; // Error + tx = ta; // Error +} + +// Flattening arrays + +type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; +type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; + +type InfiniteArray = InfiniteArray[]; + +type B0 = Flatten; +type B1 = Flatten; +type B2 = Flatten>; +type B3 = B2[0]; // Error + +// Repeating tuples + +type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; +type _TupleOf = R['length'] extends N ? R : _TupleOf; + +type TT0 = TupleOf; +type TT1 = TupleOf; +type TT2 = TupleOf; +type TT3 = TupleOf; +type TT4 = TupleOf; // Depth error + +function f22(tn: TupleOf, tm: TupleOf) { + tn = tm; + tm = tn; +} + +declare function f23(t: TupleOf): T; + +f23(['a', 'b', 'c']); // string + +// Inference from nested instantiations of same generic types + +type Box1 = { value: T }; +type Box2 = { value: T }; + +declare function foo(x: Box1>): T; + +declare let z: Box2>; + +foo(z); // string + +// Intersect tuple element types + +type Intersect = U extends [infer H, ...infer T] ? Intersect : R; + +type QQ = Intersect<[string[], number[], 7]>; + +// Infer between structurally identical recursive conditional types + +type Unpack1 = T extends (infer U)[] ? Unpack1 : T; +type Unpack2 = T extends (infer U)[] ? Unpack2 : T; + +function f20(x: Unpack1, y: Unpack2) { + x = y; + y = x; + f20(y, x); +} + +type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; +type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; + +function f21(x: Grow1<[], T>, y: Grow2<[], T>) { + f21(y, x); // Error +} + + +//// [recursiveConditionalTypes.js] +"use strict"; +// Awaiting promises +function f11(tx, ta, ux, ua) { + ta = ua; + ua = ta; // Error + ta = tx; // Error + tx = ta; // Error +} +function f22(tn, tm) { + tn = tm; + tm = tn; +} +f23(['a', 'b', 'c']); // string +foo(z); // string +function f20(x, y) { + x = y; + y = x; + f20(y, x); +} +function f21(x, y) { + f21(y, x); // Error +} + + +//// [recursiveConditionalTypes.d.ts] +declare type Awaited = T extends null | undefined ? T : T extends PromiseLike ? Awaited : T; +declare type MyPromise = { + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; +}; +declare type InfinitePromise = Promise>; +declare type P0 = Awaited | null> | undefined>>; +declare type P1 = Awaited; +declare type P2 = Awaited>; +declare function f11(tx: T, ta: Awaited, ux: U, ua: Awaited): void; +declare type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; +declare type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; +declare type InfiniteArray = InfiniteArray[]; +declare type B0 = Flatten; +declare type B1 = Flatten; +declare type B2 = Flatten>; +declare type B3 = B2[0]; +declare type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; +declare type _TupleOf = R['length'] extends N ? R : _TupleOf; +declare type TT0 = TupleOf; +declare type TT1 = TupleOf; +declare type TT2 = TupleOf; +declare type TT3 = TupleOf; +declare type TT4 = TupleOf; +declare function f22(tn: TupleOf, tm: TupleOf): void; +declare function f23(t: TupleOf): T; +declare type Box1 = { + value: T; +}; +declare type Box2 = { + value: T; +}; +declare function foo(x: Box1>): T; +declare let z: Box2>; +declare type Intersect = U extends [infer H, ...infer T] ? Intersect : R; +declare type QQ = Intersect<[string[], number[], 7]>; +declare type Unpack1 = T extends (infer U)[] ? Unpack1 : T; +declare type Unpack2 = T extends (infer U)[] ? Unpack2 : T; +declare function f20(x: Unpack1, y: Unpack2): void; +declare type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; +declare type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; +declare function f21(x: Grow1<[], T>, y: Grow2<[], T>): void; diff --git a/tests/baselines/reference/recursiveConditionalTypes.symbols b/tests/baselines/reference/recursiveConditionalTypes.symbols new file mode 100644 index 0000000000000..a45a81db2a569 --- /dev/null +++ b/tests/baselines/reference/recursiveConditionalTypes.symbols @@ -0,0 +1,352 @@ +=== tests/cases/compiler/recursiveConditionalTypes.ts === +// Awaiting promises + +type Awaited = +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 2, 13)) + + T extends null | undefined ? T : +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 2, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 2, 13)) + + T extends PromiseLike ? Awaited : +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 2, 13)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 4, 31)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 4, 31)) + + T; +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 2, 13)) + +type MyPromise = { +>MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 7, 15)) + + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; +>then : Symbol(then, Decl(recursiveConditionalTypes.ts, 7, 21)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) +>f : Symbol(f, Decl(recursiveConditionalTypes.ts, 8, 12)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 8, 17)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 7, 15)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) +>MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) +>MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) +} + +type InfinitePromise = Promise>; +>InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 11, 21)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 11, 21)) + +type P0 = Awaited | null> | undefined>>; +>P0 : Symbol(P0, Decl(recursiveConditionalTypes.ts, 11, 54)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) + +type P1 = Awaited; +>P1 : Symbol(P1, Decl(recursiveConditionalTypes.ts, 13, 83)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) + +type P2 = Awaited>; // Error +>P2 : Symbol(P2, Decl(recursiveConditionalTypes.ts, 14, 23)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1)) + +function f11(tx: T, ta: Awaited, ux: U, ua: Awaited) { +>f11 : Symbol(f11, Decl(recursiveConditionalTypes.ts, 15, 43)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 17, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 17, 15)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 17, 13)) +>tx : Symbol(tx, Decl(recursiveConditionalTypes.ts, 17, 29)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 17, 13)) +>ta : Symbol(ta, Decl(recursiveConditionalTypes.ts, 17, 35)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 17, 13)) +>ux : Symbol(ux, Decl(recursiveConditionalTypes.ts, 17, 51)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 17, 15)) +>ua : Symbol(ua, Decl(recursiveConditionalTypes.ts, 17, 58)) +>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 17, 15)) + + ta = ua; +>ta : Symbol(ta, Decl(recursiveConditionalTypes.ts, 17, 35)) +>ua : Symbol(ua, Decl(recursiveConditionalTypes.ts, 17, 58)) + + ua = ta; // Error +>ua : Symbol(ua, Decl(recursiveConditionalTypes.ts, 17, 58)) +>ta : Symbol(ta, Decl(recursiveConditionalTypes.ts, 17, 35)) + + ta = tx; // Error +>ta : Symbol(ta, Decl(recursiveConditionalTypes.ts, 17, 35)) +>tx : Symbol(tx, Decl(recursiveConditionalTypes.ts, 17, 29)) + + tx = ta; // Error +>tx : Symbol(tx, Decl(recursiveConditionalTypes.ts, 17, 29)) +>ta : Symbol(ta, Decl(recursiveConditionalTypes.ts, 17, 35)) +} + +// Flattening arrays + +type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; +>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 26, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 26, 13)) +>_Flatten : Symbol(_Flatten, Decl(recursiveConditionalTypes.ts, 26, 106)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 26, 13)) +>_Flatten : Symbol(_Flatten, Decl(recursiveConditionalTypes.ts, 26, 106)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 26, 13)) + +type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; +>_Flatten : Symbol(_Flatten, Decl(recursiveConditionalTypes.ts, 26, 106)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 27, 14)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 27, 14)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 27, 44)) +>_Flatten : Symbol(_Flatten, Decl(recursiveConditionalTypes.ts, 26, 106)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 27, 44)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 27, 14)) + +type InfiniteArray = InfiniteArray[]; +>InfiniteArray : Symbol(InfiniteArray, Decl(recursiveConditionalTypes.ts, 27, 68)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 29, 19)) +>InfiniteArray : Symbol(InfiniteArray, Decl(recursiveConditionalTypes.ts, 27, 68)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 29, 19)) + +type B0 = Flatten; +>B0 : Symbol(B0, Decl(recursiveConditionalTypes.ts, 29, 43)) +>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1)) + +type B1 = Flatten; +>B1 : Symbol(B1, Decl(recursiveConditionalTypes.ts, 31, 32)) +>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1)) + +type B2 = Flatten>; +>B2 : Symbol(B2, Decl(recursiveConditionalTypes.ts, 32, 68)) +>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1)) +>InfiniteArray : Symbol(InfiniteArray, Decl(recursiveConditionalTypes.ts, 27, 68)) + +type B3 = B2[0]; // Error +>B3 : Symbol(B3, Decl(recursiveConditionalTypes.ts, 33, 41)) +>B2 : Symbol(B2, Decl(recursiveConditionalTypes.ts, 32, 68)) + +// Repeating tuples + +type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 38, 13)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 38, 15)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 38, 15)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 38, 15)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 38, 15)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 38, 13)) +>_TupleOf : Symbol(_TupleOf, Decl(recursiveConditionalTypes.ts, 38, 102)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 38, 13)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 38, 15)) + +type _TupleOf = R['length'] extends N ? R : _TupleOf; +>_TupleOf : Symbol(_TupleOf, Decl(recursiveConditionalTypes.ts, 38, 102)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 39, 14)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 39, 16)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 39, 34)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 39, 34)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 39, 16)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 39, 34)) +>_TupleOf : Symbol(_TupleOf, Decl(recursiveConditionalTypes.ts, 38, 102)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 39, 14)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 39, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 39, 14)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 39, 34)) + +type TT0 = TupleOf; +>TT0 : Symbol(TT0, Decl(recursiveConditionalTypes.ts, 39, 112)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) + +type TT1 = TupleOf; +>TT1 : Symbol(TT1, Decl(recursiveConditionalTypes.ts, 41, 30)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) + +type TT2 = TupleOf; +>TT2 : Symbol(TT2, Decl(recursiveConditionalTypes.ts, 42, 38)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) + +type TT3 = TupleOf; +>TT3 : Symbol(TT3, Decl(recursiveConditionalTypes.ts, 43, 35)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) + +type TT4 = TupleOf; // Depth error +>TT4 : Symbol(TT4, Decl(recursiveConditionalTypes.ts, 44, 32)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) + +function f22(tn: TupleOf, tm: TupleOf) { +>f22 : Symbol(f22, Decl(recursiveConditionalTypes.ts, 45, 32)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 47, 13)) +>M : Symbol(M, Decl(recursiveConditionalTypes.ts, 47, 30)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 47, 13)) +>tn : Symbol(tn, Decl(recursiveConditionalTypes.ts, 47, 44)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 47, 13)) +>tm : Symbol(tm, Decl(recursiveConditionalTypes.ts, 47, 67)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) +>M : Symbol(M, Decl(recursiveConditionalTypes.ts, 47, 30)) + + tn = tm; +>tn : Symbol(tn, Decl(recursiveConditionalTypes.ts, 47, 44)) +>tm : Symbol(tm, Decl(recursiveConditionalTypes.ts, 47, 67)) + + tm = tn; +>tm : Symbol(tm, Decl(recursiveConditionalTypes.ts, 47, 67)) +>tn : Symbol(tn, Decl(recursiveConditionalTypes.ts, 47, 44)) +} + +declare function f23(t: TupleOf): T; +>f23 : Symbol(f23, Decl(recursiveConditionalTypes.ts, 50, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 52, 21)) +>t : Symbol(t, Decl(recursiveConditionalTypes.ts, 52, 24)) +>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 52, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 52, 21)) + +f23(['a', 'b', 'c']); // string +>f23 : Symbol(f23, Decl(recursiveConditionalTypes.ts, 50, 1)) + +// Inference from nested instantiations of same generic types + +type Box1 = { value: T }; +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 10)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 58, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 10)) + +type Box2 = { value: T }; +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 10)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 59, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 10)) + +declare function foo(x: Box1>): T; +>foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 59, 28)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 61, 24)) +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) + +declare let z: Box2>; +>z : Symbol(z, Decl(recursiveConditionalTypes.ts, 63, 11)) +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) + +foo(z); // string +>foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 59, 28)) +>z : Symbol(z, Decl(recursiveConditionalTypes.ts, 63, 11)) + +// Intersect tuple element types + +type Intersect = U extends [infer H, ...infer T] ? Intersect : R; +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 69, 15)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 69, 15)) +>H : Symbol(H, Decl(recursiveConditionalTypes.ts, 69, 63)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 69, 75)) +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 69, 75)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) +>H : Symbol(H, Decl(recursiveConditionalTypes.ts, 69, 63)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) + +type QQ = Intersect<[string[], number[], 7]>; +>QQ : Symbol(QQ, Decl(recursiveConditionalTypes.ts, 69, 105)) +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) + +// Infer between structurally identical recursive conditional types + +type Unpack1 = T extends (infer U)[] ? Unpack1 : T; +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 75, 34)) +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 75, 34)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) + +type Unpack2 = T extends (infer U)[] ? Unpack2 : T; +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 76, 34)) +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 76, 34)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) + +function f20(x: Unpack1, y: Unpack2) { +>f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 76, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 78, 15)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) + + x = y; +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) + + y = x; +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) + + f20(y, x); +>f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 76, 57)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) +} + +type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) + +type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) + +function f21(x: Grow1<[], T>, y: Grow2<[], T>) { +>f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 85, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 87, 31)) +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 87, 47)) +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) + + f21(y, x); // Error +>f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 85, 105)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 87, 47)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 87, 31)) +} + diff --git a/tests/baselines/reference/recursiveConditionalTypes.types b/tests/baselines/reference/recursiveConditionalTypes.types new file mode 100644 index 0000000000000..ca6f7d1689832 --- /dev/null +++ b/tests/baselines/reference/recursiveConditionalTypes.types @@ -0,0 +1,215 @@ +=== tests/cases/compiler/recursiveConditionalTypes.ts === +// Awaiting promises + +type Awaited = +>Awaited : Awaited + + T extends null | undefined ? T : +>null : null + + T extends PromiseLike ? Awaited : + T; + +type MyPromise = { +>MyPromise : MyPromise + + then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; +>then : (f: ((value: T) => U | MyPromise) | null | undefined) => MyPromise +>f : ((value: T) => U | MyPromise) | null | undefined +>value : T +>null : null +} + +type InfinitePromise = Promise>; +>InfinitePromise : InfinitePromise + +type P0 = Awaited | null> | undefined>>; +>P0 : string | number | null | undefined +>null : null + +type P1 = Awaited; +>P1 : any + +type P2 = Awaited>; // Error +>P2 : any + +function f11(tx: T, ta: Awaited, ux: U, ua: Awaited) { +>f11 : (tx: T, ta: Awaited, ux: U, ua: Awaited) => void +>tx : T +>ta : Awaited +>ux : U +>ua : Awaited + + ta = ua; +>ta = ua : Awaited +>ta : Awaited +>ua : Awaited + + ua = ta; // Error +>ua = ta : Awaited +>ua : Awaited +>ta : Awaited + + ta = tx; // Error +>ta = tx : T +>ta : Awaited +>tx : T + + tx = ta; // Error +>tx = ta : Awaited +>tx : T +>ta : Awaited +} + +// Flattening arrays + +type Flatten = T extends unknown[] ? _Flatten[] : readonly _Flatten[]; +>Flatten : Flatten + +type _Flatten = T extends readonly (infer U)[] ? _Flatten : T; +>_Flatten : _Flatten + +type InfiniteArray = InfiniteArray[]; +>InfiniteArray : InfiniteArray + +type B0 = Flatten; +>B0 : string[] + +type B1 = Flatten; +>B1 : string[] | readonly (number | boolean)[] + +type B2 = Flatten>; +>B2 : any[] + +type B3 = B2[0]; // Error +>B3 : any + +// Repeating tuples + +type TupleOf = N extends N ? number extends N ? T[] : _TupleOf : never; +>TupleOf : TupleOf + +type _TupleOf = R['length'] extends N ? R : _TupleOf; +>_TupleOf : _TupleOf + +type TT0 = TupleOf; +>TT0 : [string, string, string, string] + +type TT1 = TupleOf; +>TT1 : [] | [number, number] | [number, number, number, number] + +type TT2 = TupleOf; +>TT2 : number[] + +type TT3 = TupleOf; +>TT3 : number[] + +type TT4 = TupleOf; // Depth error +>TT4 : any + +function f22(tn: TupleOf, tm: TupleOf) { +>f22 : (tn: TupleOf, tm: TupleOf) => void +>tn : TupleOf +>tm : TupleOf + + tn = tm; +>tn = tm : TupleOf +>tn : TupleOf +>tm : TupleOf + + tm = tn; +>tm = tn : TupleOf +>tm : TupleOf +>tn : TupleOf +} + +declare function f23(t: TupleOf): T; +>f23 : (t: [T, T, T]) => T +>t : [T, T, T] + +f23(['a', 'b', 'c']); // string +>f23(['a', 'b', 'c']) : string +>f23 : (t: [T, T, T]) => T +>['a', 'b', 'c'] : [string, string, string] +>'a' : "a" +>'b' : "b" +>'c' : "c" + +// Inference from nested instantiations of same generic types + +type Box1 = { value: T }; +>Box1 : Box1 +>value : T + +type Box2 = { value: T }; +>Box2 : Box2 +>value : T + +declare function foo(x: Box1>): T; +>foo : (x: Box1>) => T +>x : Box1> + +declare let z: Box2>; +>z : Box2> + +foo(z); // string +>foo(z) : string +>foo : (x: Box1>) => T +>z : Box2> + +// Intersect tuple element types + +type Intersect = U extends [infer H, ...infer T] ? Intersect : R; +>Intersect : Intersect + +type QQ = Intersect<[string[], number[], 7]>; +>QQ : string[] & number[] & 7 + +// Infer between structurally identical recursive conditional types + +type Unpack1 = T extends (infer U)[] ? Unpack1 : T; +>Unpack1 : Unpack1 + +type Unpack2 = T extends (infer U)[] ? Unpack2 : T; +>Unpack2 : Unpack2 + +function f20(x: Unpack1, y: Unpack2) { +>f20 : (x: Unpack1, y: Unpack2) => void +>x : Unpack1 +>y : Unpack2 + + x = y; +>x = y : Unpack2 +>x : Unpack1 +>y : Unpack2 + + y = x; +>y = x : Unpack1 +>y : Unpack2 +>x : Unpack1 + + f20(y, x); +>f20(y, x) : void +>f20 : (x: Unpack1, y: Unpack2) => void +>y : Unpack2 +>x : Unpack1 +} + +type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; +>Grow1 : Grow1 + +type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; +>Grow2 : Grow2 + +function f21(x: Grow1<[], T>, y: Grow2<[], T>) { +>f21 : (x: Grow1<[], T>, y: Grow2<[], T>) => void +>x : Grow1<[], T> +>y : Grow2<[], T> + + f21(y, x); // Error +>f21(y, x) : void +>f21 : (x: Grow1<[], T>, y: Grow2<[], T>) => void +>y : Grow2<[], T> +>x : Grow1<[], T> +} + From 6c11cf462e975ade7cebae80675b609345bea514 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 13 Aug 2020 13:07:18 -0700 Subject: [PATCH 08/12] Revise recursion tracking in type inference --- src/compiler/checker.ts | 64 ++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9456e4eb4c191..45893eae63910 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18201,15 +18201,24 @@ namespace ts { return false; } - function getRecursionIdentity(type: Type) { + // Types with constituents that could circularly reference the type have a recursion identity. The recursion + // identity is some object that is common to instantiations of the type with the same origin. + function getRecursionIdentity(type: Type): object | undefined { if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) { - if (type.symbol) { - // We track all object types that have an associated symbol (representing the origin of the type) + if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node) { + // Deferred type references are tracked through their associated AST node. This gives us finer + // granularity than using their associated target because each manifest type reference has a + // unique AST node. + return (type as TypeReference).node; + } + if (type.symbol && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)) { + // We track all object types that have an associated symbol (representing the origin of the type), but + // exclude the static side of classes from this check since it shares its symbol with the instance side. return type.symbol; } - if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node || isTupleType(type)) { - // Deferred type references and tuple types are tracked through their target type - return (type as TypeReference).target; + if (isTupleType(type)) { + // Tuple types are tracked through their target type + return type.target; } } if (type.flags & TypeFlags.IndexedAccess) { @@ -19283,7 +19292,9 @@ namespace ts { let inferencePriority = InferencePriority.MaxValue; let allowComplexConstraintInference = true; let visited: ESMap; - let targetStack: Type[]; + let sourceStack: object[]; + let targetStack: object[]; + let expandingFlags = ExpandingFlags.None; inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type): void { @@ -19406,7 +19417,7 @@ namespace ts { // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine const simplified = getSimplifiedType(target, /*writing*/ false); if (simplified !== target) { - invokeWithDepthLimit(source, simplified, inferFromTypes); + invokeOnce(source, simplified, inferFromTypes); } else if (target.flags & TypeFlags.IndexedAccess) { const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); @@ -19415,7 +19426,7 @@ namespace ts { if (indexType.flags & TypeFlags.Instantiable) { const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); if (simplified && simplified !== target) { - invokeWithDepthLimit(source, simplified, inferFromTypes); + invokeOnce(source, simplified, inferFromTypes); } } } @@ -19443,7 +19454,7 @@ namespace ts { inferFromTypes((source).indexType, (target).indexType); } else if (target.flags & TypeFlags.Conditional) { - invokeWithDepthLimit(source, target, inferToConditionalType); + invokeOnce(source, target, inferToConditionalType); } else if (target.flags & TypeFlags.UnionOrIntersection) { inferToMultipleTypes(source, (target).types, target.flags); @@ -19476,7 +19487,7 @@ namespace ts { source = apparentSource; } if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) { - invokeWithDepthLimit(source, target, inferFromObjectTypes); + invokeOnce(source, target, inferFromObjectTypes); } } if (source.flags & TypeFlags.Simplifiable) { @@ -19494,7 +19505,7 @@ namespace ts { priority = savePriority; } - function invokeWithDepthLimit(source: Type, target: Type, action: (source: Type, target: Type) => void) { + function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) { const key = source.id + "," + target.id; const status = visited && visited.get(key); if (status !== undefined) { @@ -19504,17 +19515,24 @@ namespace ts { (visited || (visited = new Map())).set(key, InferencePriority.Circularity); const saveInferencePriority = inferencePriority; inferencePriority = InferencePriority.MaxValue; - // It is possible for recursion to originate in generative types that create infinitely deep instantiations, - // with unique identities, for example 'type RecArray = T | Array>'. We explore up to five - // nested instantiations of such types using the same isDeeplyNestedType check as recursiveTypeRelatedTo. - (targetStack || (targetStack = [])).push(target); - if (!isDeeplyNestedType(target, targetStack, targetStack.length)) { + // We stop inferring and report a circularity if we encounter duplicate recursion identities on both + // the source side and the target side. + const saveExpandingFlags = expandingFlags; + const sourceIdentity = getRecursionIdentity(source); + const targetIdentity = getRecursionIdentity(target); + if (sourceIdentity && contains(sourceStack, sourceIdentity)) expandingFlags |= ExpandingFlags.Source; + if (targetIdentity && contains(targetStack, targetIdentity)) expandingFlags |= ExpandingFlags.Target; + if (expandingFlags !== ExpandingFlags.Both) { + if (sourceIdentity) (sourceStack || (sourceStack = [])).push(sourceIdentity); + if (targetIdentity) (targetStack || (targetStack = [])).push(targetIdentity); action(source, target); + if (targetIdentity) targetStack.pop(); + if (sourceIdentity) sourceStack.pop(); } else { inferencePriority = InferencePriority.Circularity; } - targetStack.pop(); + expandingFlags = saveExpandingFlags; visited.set(key, inferencePriority); inferencePriority = Math.min(inferencePriority, saveInferencePriority); } @@ -19710,12 +19728,12 @@ namespace ts { return false; } - function inferToConditionalType(source: ConditionalType, target: ConditionalType) { + function inferToConditionalType(source: Type, target: ConditionalType) { if (source.flags & TypeFlags.Conditional) { - inferFromTypes(source.checkType, target.checkType); - inferFromTypes(source.extendsType, target.extendsType); - inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target)); - inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); + inferFromTypes((source).checkType, target.checkType); + inferFromTypes((source).extendsType, target.extendsType); + inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target)); + inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); } else { const savePriority = priority; From 5b45f42c79f9ef9b43ce024d0505b3cd8e07d2b5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 13 Aug 2020 13:16:11 -0700 Subject: [PATCH 09/12] Revise tests --- tests/cases/compiler/recursiveConditionalTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/compiler/recursiveConditionalTypes.ts b/tests/cases/compiler/recursiveConditionalTypes.ts index 33f7e63c47791..010ad782b8dad 100644 --- a/tests/cases/compiler/recursiveConditionalTypes.ts +++ b/tests/cases/compiler/recursiveConditionalTypes.ts @@ -9,7 +9,7 @@ type Awaited = T; type MyPromise = { - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; } type InfinitePromise = Promise>; @@ -66,7 +66,7 @@ declare function foo(x: Box1>): T; declare let z: Box2>; -foo(z); // string +foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) // Intersect tuple element types From fed0e8c71b505a73ae536d68a60d61edf6445a47 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 13 Aug 2020 13:16:19 -0700 Subject: [PATCH 10/12] Accept new baselines --- .../reference/recursiveConditionalTypes.errors.txt | 4 ++-- tests/baselines/reference/recursiveConditionalTypes.js | 8 ++++---- .../reference/recursiveConditionalTypes.symbols | 6 +++--- .../reference/recursiveConditionalTypes.types | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/baselines/reference/recursiveConditionalTypes.errors.txt b/tests/baselines/reference/recursiveConditionalTypes.errors.txt index 2b084c3554cc2..f78762ac39a84 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.errors.txt +++ b/tests/baselines/reference/recursiveConditionalTypes.errors.txt @@ -41,7 +41,7 @@ tests/cases/compiler/recursiveConditionalTypes.ts(89,9): error TS2345: Argument T; type MyPromise = { - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; } type InfinitePromise = Promise>; @@ -129,7 +129,7 @@ tests/cases/compiler/recursiveConditionalTypes.ts(89,9): error TS2345: Argument declare let z: Box2>; - foo(z); // string + foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) // Intersect tuple element types diff --git a/tests/baselines/reference/recursiveConditionalTypes.js b/tests/baselines/reference/recursiveConditionalTypes.js index 4d96f319b3e44..2b65bcbbd3a8f 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.js +++ b/tests/baselines/reference/recursiveConditionalTypes.js @@ -7,7 +7,7 @@ type Awaited = T; type MyPromise = { - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; } type InfinitePromise = Promise>; @@ -64,7 +64,7 @@ declare function foo(x: Box1>): T; declare let z: Box2>; -foo(z); // string +foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) // Intersect tuple element types @@ -105,7 +105,7 @@ function f22(tn, tm) { tm = tn; } f23(['a', 'b', 'c']); // string -foo(z); // string +foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) function f20(x, y) { x = y; y = x; @@ -119,7 +119,7 @@ function f21(x, y) { //// [recursiveConditionalTypes.d.ts] declare type Awaited = T extends null | undefined ? T : T extends PromiseLike ? Awaited : T; declare type MyPromise = { - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; }; declare type InfinitePromise = Promise>; declare type P0 = Awaited | null> | undefined>>; diff --git a/tests/baselines/reference/recursiveConditionalTypes.symbols b/tests/baselines/reference/recursiveConditionalTypes.symbols index a45a81db2a569..562c8edb442c4 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.symbols +++ b/tests/baselines/reference/recursiveConditionalTypes.symbols @@ -23,14 +23,14 @@ type MyPromise = { >MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) >T : Symbol(T, Decl(recursiveConditionalTypes.ts, 7, 15)) - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; >then : Symbol(then, Decl(recursiveConditionalTypes.ts, 7, 21)) >U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) >f : Symbol(f, Decl(recursiveConditionalTypes.ts, 8, 12)) >value : Symbol(value, Decl(recursiveConditionalTypes.ts, 8, 17)) >T : Symbol(T, Decl(recursiveConditionalTypes.ts, 7, 15)) >U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) ->MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) >U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) >MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) >U : Symbol(U, Decl(recursiveConditionalTypes.ts, 8, 9)) @@ -243,7 +243,7 @@ declare let z: Box2>; >Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) >Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) -foo(z); // string +foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) >foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 59, 28)) >z : Symbol(z, Decl(recursiveConditionalTypes.ts, 63, 11)) diff --git a/tests/baselines/reference/recursiveConditionalTypes.types b/tests/baselines/reference/recursiveConditionalTypes.types index ca6f7d1689832..8e4393b4d9599 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.types +++ b/tests/baselines/reference/recursiveConditionalTypes.types @@ -13,9 +13,9 @@ type Awaited = type MyPromise = { >MyPromise : MyPromise - then(f: ((value: T) => U | MyPromise) | null | undefined): MyPromise; ->then : (f: ((value: T) => U | MyPromise) | null | undefined) => MyPromise ->f : ((value: T) => U | MyPromise) | null | undefined + then(f: ((value: T) => U | PromiseLike) | null | undefined): MyPromise; +>then : (f: ((value: T) => U | PromiseLike) | null | undefined) => MyPromise +>f : ((value: T) => U | PromiseLike) | null | undefined >value : T >null : null } @@ -152,8 +152,8 @@ declare function foo(x: Box1>): T; declare let z: Box2>; >z : Box2> -foo(z); // string ->foo(z) : string +foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) +>foo(z) : unknown >foo : (x: Box1>) => T >z : Box2> From 199d568785a48a37031c68bf252d1027cb4e5abb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 15 Aug 2020 17:50:53 -0700 Subject: [PATCH 11/12] Add more tests --- .../compiler/recursiveConditionalTypes.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/cases/compiler/recursiveConditionalTypes.ts b/tests/cases/compiler/recursiveConditionalTypes.ts index 010ad782b8dad..f3d88cd95728e 100644 --- a/tests/cases/compiler/recursiveConditionalTypes.ts +++ b/tests/cases/compiler/recursiveConditionalTypes.ts @@ -1,5 +1,6 @@ // @strict: true // @declaration: true +// @target: esnext // Awaiting promises @@ -57,6 +58,33 @@ declare function f23(t: TupleOf): T; f23(['a', 'b', 'c']); // string +// Inference to recursive type + +interface Box { value: T }; +type RecBox = T | Box>; +type InfBox = Box>; + +declare function unbox(box: RecBox): T + +type T1 = Box; +type T2 = Box; +type T3 = Box; +type T4 = Box; +type T5 = Box; +type T6 = Box; + +declare let b1: Box>>>>>; +declare let b2: T6; +declare let b3: InfBox; +declare let b4: { value: { value: { value: typeof b4 }}}; + +unbox(b1); // string +unbox(b2); // string +unbox(b3); // InfBox +unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number +unbox(b4); // { value: { value: typeof b4 }} +unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... } + // Inference from nested instantiations of same generic types type Box1 = { value: T }; From 324b3fbb65da8317b39a3fa85b1fe4f41331006e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 15 Aug 2020 17:51:00 -0700 Subject: [PATCH 12/12] Accept new baselines --- .../recursiveConditionalTypes.errors.txt | 31 +- .../reference/recursiveConditionalTypes.js | 56 ++++ .../recursiveConditionalTypes.symbols | 307 ++++++++++++------ .../reference/recursiveConditionalTypes.types | 97 ++++++ 4 files changed, 393 insertions(+), 98 deletions(-) diff --git a/tests/baselines/reference/recursiveConditionalTypes.errors.txt b/tests/baselines/reference/recursiveConditionalTypes.errors.txt index f78762ac39a84..907919a46bbd3 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.errors.txt +++ b/tests/baselines/reference/recursiveConditionalTypes.errors.txt @@ -21,8 +21,8 @@ tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'Tup Type 'number extends N ? number[] : _TupleOf' is not assignable to type 'TupleOf'. Type 'number[] | _TupleOf' is not assignable to type 'TupleOf'. Type 'number[]' is not assignable to type 'TupleOf'. -tests/cases/compiler/recursiveConditionalTypes.ts(89,5): error TS2589: Type instantiation is excessively deep and possibly infinite. -tests/cases/compiler/recursiveConditionalTypes.ts(89,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'. +tests/cases/compiler/recursiveConditionalTypes.ts(116,5): error TS2589: Type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'. Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'. Type '[]' is not assignable to type 'Grow1<[], T>'. Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'. @@ -120,6 +120,33 @@ tests/cases/compiler/recursiveConditionalTypes.ts(89,9): error TS2345: Argument f23(['a', 'b', 'c']); // string + // Inference to recursive type + + interface Box { value: T }; + type RecBox = T | Box>; + type InfBox = Box>; + + declare function unbox(box: RecBox): T + + type T1 = Box; + type T2 = Box; + type T3 = Box; + type T4 = Box; + type T5 = Box; + type T6 = Box; + + declare let b1: Box>>>>>; + declare let b2: T6; + declare let b3: InfBox; + declare let b4: { value: { value: { value: typeof b4 }}}; + + unbox(b1); // string + unbox(b2); // string + unbox(b3); // InfBox + unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number + unbox(b4); // { value: { value: typeof b4 }} + unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... } + // Inference from nested instantiations of same generic types type Box1 = { value: T }; diff --git a/tests/baselines/reference/recursiveConditionalTypes.js b/tests/baselines/reference/recursiveConditionalTypes.js index 2b65bcbbd3a8f..13dd79891645e 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.js +++ b/tests/baselines/reference/recursiveConditionalTypes.js @@ -55,6 +55,33 @@ declare function f23(t: TupleOf): T; f23(['a', 'b', 'c']); // string +// Inference to recursive type + +interface Box { value: T }; +type RecBox = T | Box>; +type InfBox = Box>; + +declare function unbox(box: RecBox): T + +type T1 = Box; +type T2 = Box; +type T3 = Box; +type T4 = Box; +type T5 = Box; +type T6 = Box; + +declare let b1: Box>>>>>; +declare let b2: T6; +declare let b3: InfBox; +declare let b4: { value: { value: { value: typeof b4 }}}; + +unbox(b1); // string +unbox(b2); // string +unbox(b3); // InfBox +unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number +unbox(b4); // { value: { value: typeof b4 }} +unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... } + // Inference from nested instantiations of same generic types type Box1 = { value: T }; @@ -105,6 +132,13 @@ function f22(tn, tm) { tm = tn; } f23(['a', 'b', 'c']); // string +; +unbox(b1); // string +unbox(b2); // string +unbox(b3); // InfBox +unbox({ value: { value: { value: { value: { value: { value: 5 } } } } } }); // number +unbox(b4); // { value: { value: typeof b4 }} +unbox({ value: { value: { get value() { return this; } } } }); // { readonly value: ... } foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) function f20(x, y) { x = y; @@ -142,6 +176,28 @@ declare type TT3 = TupleOf; declare type TT4 = TupleOf; declare function f22(tn: TupleOf, tm: TupleOf): void; declare function f23(t: TupleOf): T; +interface Box { + value: T; +} +declare type RecBox = T | Box>; +declare type InfBox = Box>; +declare function unbox(box: RecBox): T; +declare type T1 = Box; +declare type T2 = Box; +declare type T3 = Box; +declare type T4 = Box; +declare type T5 = Box; +declare type T6 = Box; +declare let b1: Box>>>>>; +declare let b2: T6; +declare let b3: InfBox; +declare let b4: { + value: { + value: { + value: typeof b4; + }; + }; +}; declare type Box1 = { value: T; }; diff --git a/tests/baselines/reference/recursiveConditionalTypes.symbols b/tests/baselines/reference/recursiveConditionalTypes.symbols index 562c8edb442c4..c0154a1e1a532 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.symbols +++ b/tests/baselines/reference/recursiveConditionalTypes.symbols @@ -39,15 +39,15 @@ type MyPromise = { type InfinitePromise = Promise>; >InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1)) >T : Symbol(T, Decl(recursiveConditionalTypes.ts, 11, 21)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) >InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1)) >T : Symbol(T, Decl(recursiveConditionalTypes.ts, 11, 21)) type P0 = Awaited | null> | undefined>>; >P0 : Symbol(P0, Decl(recursiveConditionalTypes.ts, 11, 54)) >Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) ->Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) >MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6)) type P1 = Awaited; @@ -215,138 +215,253 @@ declare function f23(t: TupleOf): T; f23(['a', 'b', 'c']); // string >f23 : Symbol(f23, Decl(recursiveConditionalTypes.ts, 50, 1)) +// Inference to recursive type + +interface Box { value: T }; +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 14)) +>value : Symbol(Box.value, Decl(recursiveConditionalTypes.ts, 58, 18)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 14)) + +type RecBox = T | Box>; +>RecBox : Symbol(RecBox, Decl(recursiveConditionalTypes.ts, 58, 30)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 12)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 12)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>RecBox : Symbol(RecBox, Decl(recursiveConditionalTypes.ts, 58, 30)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 12)) + +type InfBox = Box>; +>InfBox : Symbol(InfBox, Decl(recursiveConditionalTypes.ts, 59, 36)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 60, 12)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>InfBox : Symbol(InfBox, Decl(recursiveConditionalTypes.ts, 59, 36)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 60, 12)) + +declare function unbox(box: RecBox): T +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 62, 23)) +>box : Symbol(box, Decl(recursiveConditionalTypes.ts, 62, 26)) +>RecBox : Symbol(RecBox, Decl(recursiveConditionalTypes.ts, 58, 30)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 62, 23)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 62, 23)) + +type T1 = Box; +>T1 : Symbol(T1, Decl(recursiveConditionalTypes.ts, 62, 44)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) + +type T2 = Box; +>T2 : Symbol(T2, Decl(recursiveConditionalTypes.ts, 64, 22)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T1 : Symbol(T1, Decl(recursiveConditionalTypes.ts, 62, 44)) + +type T3 = Box; +>T3 : Symbol(T3, Decl(recursiveConditionalTypes.ts, 65, 18)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T2 : Symbol(T2, Decl(recursiveConditionalTypes.ts, 64, 22)) + +type T4 = Box; +>T4 : Symbol(T4, Decl(recursiveConditionalTypes.ts, 66, 18)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T3 : Symbol(T3, Decl(recursiveConditionalTypes.ts, 65, 18)) + +type T5 = Box; +>T5 : Symbol(T5, Decl(recursiveConditionalTypes.ts, 67, 18)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T4 : Symbol(T4, Decl(recursiveConditionalTypes.ts, 66, 18)) + +type T6 = Box; +>T6 : Symbol(T6, Decl(recursiveConditionalTypes.ts, 68, 18)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>T5 : Symbol(T5, Decl(recursiveConditionalTypes.ts, 67, 18)) + +declare let b1: Box>>>>>; +>b1 : Symbol(b1, Decl(recursiveConditionalTypes.ts, 71, 11)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) +>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21)) + +declare let b2: T6; +>b2 : Symbol(b2, Decl(recursiveConditionalTypes.ts, 72, 11)) +>T6 : Symbol(T6, Decl(recursiveConditionalTypes.ts, 68, 18)) + +declare let b3: InfBox; +>b3 : Symbol(b3, Decl(recursiveConditionalTypes.ts, 73, 11)) +>InfBox : Symbol(InfBox, Decl(recursiveConditionalTypes.ts, 59, 36)) + +declare let b4: { value: { value: { value: typeof b4 }}}; +>b4 : Symbol(b4, Decl(recursiveConditionalTypes.ts, 74, 11)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 74, 17)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 74, 26)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 74, 35)) +>b4 : Symbol(b4, Decl(recursiveConditionalTypes.ts, 74, 11)) + +unbox(b1); // string +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>b1 : Symbol(b1, Decl(recursiveConditionalTypes.ts, 71, 11)) + +unbox(b2); // string +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>b2 : Symbol(b2, Decl(recursiveConditionalTypes.ts, 72, 11)) + +unbox(b3); // InfBox +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>b3 : Symbol(b3, Decl(recursiveConditionalTypes.ts, 73, 11)) + +unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 7)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 16)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 25)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 34)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 43)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 79, 52)) + +unbox(b4); // { value: { value: typeof b4 }} +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>b4 : Symbol(b4, Decl(recursiveConditionalTypes.ts, 74, 11)) + +unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... } +>unbox : Symbol(unbox, Decl(recursiveConditionalTypes.ts, 60, 32)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 81, 7)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 81, 16)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 81, 25)) + // Inference from nested instantiations of same generic types type Box1 = { value: T }; ->Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 10)) ->value : Symbol(value, Decl(recursiveConditionalTypes.ts, 58, 16)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 58, 10)) +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 81, 60)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 10)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 85, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 10)) type Box2 = { value: T }; ->Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 10)) ->value : Symbol(value, Decl(recursiveConditionalTypes.ts, 59, 16)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 59, 10)) +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 85, 28)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 86, 10)) +>value : Symbol(value, Decl(recursiveConditionalTypes.ts, 86, 16)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 86, 10)) declare function foo(x: Box1>): T; ->foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 59, 28)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 61, 24)) ->Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) ->Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 54, 21)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 61, 21)) +>foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 86, 28)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 88, 21)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 88, 24)) +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 81, 60)) +>Box1 : Symbol(Box1, Decl(recursiveConditionalTypes.ts, 81, 60)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 88, 21)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 88, 21)) declare let z: Box2>; ->z : Symbol(z, Decl(recursiveConditionalTypes.ts, 63, 11)) ->Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) ->Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 58, 28)) +>z : Symbol(z, Decl(recursiveConditionalTypes.ts, 90, 11)) +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 85, 28)) +>Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 85, 28)) foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) ->foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 59, 28)) ->z : Symbol(z, Decl(recursiveConditionalTypes.ts, 63, 11)) +>foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 86, 28)) +>z : Symbol(z, Decl(recursiveConditionalTypes.ts, 90, 11)) // Intersect tuple element types type Intersect = U extends [infer H, ...infer T] ? Intersect : R; ->Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 69, 15)) ->R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 69, 15)) ->H : Symbol(H, Decl(recursiveConditionalTypes.ts, 69, 63)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 69, 75)) ->Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 69, 75)) ->R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) ->H : Symbol(H, Decl(recursiveConditionalTypes.ts, 69, 63)) ->R : Symbol(R, Decl(recursiveConditionalTypes.ts, 69, 31)) +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 92, 7)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 96, 15)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 96, 31)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 96, 15)) +>H : Symbol(H, Decl(recursiveConditionalTypes.ts, 96, 63)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 96, 75)) +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 92, 7)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 96, 75)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 96, 31)) +>H : Symbol(H, Decl(recursiveConditionalTypes.ts, 96, 63)) +>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 96, 31)) type QQ = Intersect<[string[], number[], 7]>; ->QQ : Symbol(QQ, Decl(recursiveConditionalTypes.ts, 69, 105)) ->Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 65, 7)) +>QQ : Symbol(QQ, Decl(recursiveConditionalTypes.ts, 96, 105)) +>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 92, 7)) // Infer between structurally identical recursive conditional types type Unpack1 = T extends (infer U)[] ? Unpack1 : T; ->Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 75, 34)) ->Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 75, 34)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 75, 13)) +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 98, 45)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 102, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 102, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 102, 34)) +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 98, 45)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 102, 34)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 102, 13)) type Unpack2 = T extends (infer U)[] ? Unpack2 : T; ->Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 76, 34)) ->Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 76, 34)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 76, 13)) +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 102, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 103, 13)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 103, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 103, 34)) +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 102, 57)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 103, 34)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 103, 13)) function f20(x: Unpack1, y: Unpack2) { ->f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 76, 57)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) ->U : Symbol(U, Decl(recursiveConditionalTypes.ts, 78, 15)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) ->Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 71, 45)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) ->Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 75, 57)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 78, 13)) +>f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 103, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 105, 13)) +>U : Symbol(U, Decl(recursiveConditionalTypes.ts, 105, 15)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 105, 13)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 105, 29)) +>Unpack1 : Symbol(Unpack1, Decl(recursiveConditionalTypes.ts, 98, 45)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 105, 13)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43)) +>Unpack2 : Symbol(Unpack2, Decl(recursiveConditionalTypes.ts, 102, 57)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 105, 13)) x = y; ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 105, 29)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43)) y = x; ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 105, 29)) f20(y, x); ->f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 76, 57)) ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 78, 43)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 78, 29)) +>f20 : Symbol(f20, Decl(recursiveConditionalTypes.ts, 103, 57)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 105, 29)) } type Grow1 = T['length'] extends N ? T : Grow1<[number, ...T], N>; ->Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) ->Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 84, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 84, 31)) +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 109, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 111, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 111, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 111, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 111, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 111, 11)) +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 109, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 111, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 111, 31)) type Grow2 = T['length'] extends N ? T : Grow2<[string, ...T], N>; ->Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) ->Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 85, 11)) ->N : Symbol(N, Decl(recursiveConditionalTypes.ts, 85, 31)) +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 111, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 112, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 112, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 112, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 112, 31)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 112, 11)) +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 111, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 112, 11)) +>N : Symbol(N, Decl(recursiveConditionalTypes.ts, 112, 31)) function f21(x: Grow1<[], T>, y: Grow2<[], T>) { ->f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 85, 105)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 87, 31)) ->Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 82, 1)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 87, 47)) ->Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 84, 105)) ->T : Symbol(T, Decl(recursiveConditionalTypes.ts, 87, 13)) +>f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 112, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 114, 13)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 114, 31)) +>Grow1 : Symbol(Grow1, Decl(recursiveConditionalTypes.ts, 109, 1)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 114, 13)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 114, 47)) +>Grow2 : Symbol(Grow2, Decl(recursiveConditionalTypes.ts, 111, 105)) +>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 114, 13)) f21(y, x); // Error ->f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 85, 105)) ->y : Symbol(y, Decl(recursiveConditionalTypes.ts, 87, 47)) ->x : Symbol(x, Decl(recursiveConditionalTypes.ts, 87, 31)) +>f21 : Symbol(f21, Decl(recursiveConditionalTypes.ts, 112, 105)) +>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 114, 47)) +>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 114, 31)) } diff --git a/tests/baselines/reference/recursiveConditionalTypes.types b/tests/baselines/reference/recursiveConditionalTypes.types index 8e4393b4d9599..a797f76b83de5 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.types +++ b/tests/baselines/reference/recursiveConditionalTypes.types @@ -135,6 +135,103 @@ f23(['a', 'b', 'c']); // string >'b' : "b" >'c' : "c" +// Inference to recursive type + +interface Box { value: T }; +>value : T + +type RecBox = T | Box>; +>RecBox : RecBox + +type InfBox = Box>; +>InfBox : InfBox + +declare function unbox(box: RecBox): T +>unbox : (box: RecBox) => T +>box : RecBox + +type T1 = Box; +>T1 : T1 + +type T2 = Box; +>T2 : T2 + +type T3 = Box; +>T3 : T3 + +type T4 = Box; +>T4 : T4 + +type T5 = Box; +>T5 : T5 + +type T6 = Box; +>T6 : T6 + +declare let b1: Box>>>>>; +>b1 : Box>>>>> + +declare let b2: T6; +>b2 : T6 + +declare let b3: InfBox; +>b3 : InfBox + +declare let b4: { value: { value: { value: typeof b4 }}}; +>b4 : { value: { value: { value: typeof b4; };}; } +>value : { value: { value: typeof b4;}; } +>value : { value: typeof b4; } +>value : { value: { value: { value: typeof b4; }; }; } +>b4 : { value: { value: { value: any; }; }; } + +unbox(b1); // string +>unbox(b1) : string +>unbox : (box: RecBox) => T +>b1 : Box>>>>> + +unbox(b2); // string +>unbox(b2) : string +>unbox : (box: RecBox) => T +>b2 : T6 + +unbox(b3); // InfBox +>unbox(b3) : InfBox +>unbox : (box: RecBox) => T +>b3 : InfBox + +unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number +>unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}) : number +>unbox : (box: RecBox) => T +>{ value: { value: { value: { value: { value: { value: 5 }}}}}} : { value: { value: { value: { value: { value: { value: number; }; }; }; }; }; } +>value : { value: { value: { value: { value: { value: number; }; }; }; }; } +>{ value: { value: { value: { value: { value: 5 }}}}} : { value: { value: { value: { value: { value: number; }; }; }; }; } +>value : { value: { value: { value: { value: number; }; }; }; } +>{ value: { value: { value: { value: 5 }}}} : { value: { value: { value: { value: number; }; }; }; } +>value : { value: { value: { value: number; }; }; } +>{ value: { value: { value: 5 }}} : { value: { value: { value: number; }; }; } +>value : { value: { value: number; }; } +>{ value: { value: 5 }} : { value: { value: number; }; } +>value : { value: number; } +>{ value: 5 } : { value: number; } +>value : number +>5 : 5 + +unbox(b4); // { value: { value: typeof b4 }} +>unbox(b4) : { value: { value: { value: any; }; }; } +>unbox : (box: RecBox) => T +>b4 : { value: { value: { value: any; }; }; } + +unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... } +>unbox({ value: { value: { get value() { return this; } }}}) : { readonly value: { readonly value: any; }; } +>unbox : (box: RecBox) => T +>{ value: { value: { get value() { return this; } }}} : { value: { value: { readonly value: { readonly value: any; }; }; }; } +>value : { value: { readonly value: { readonly value: any; }; }; } +>{ value: { get value() { return this; } }} : { value: { readonly value: { readonly value: any; }; }; } +>value : { readonly value: { readonly value: any; }; } +>{ get value() { return this; } } : { readonly value: { readonly value: any; }; } +>value : { readonly value: any; } +>this : { readonly value: any; } | { readonly value: { readonly value: any; }; } | Box> + // Inference from nested instantiations of same generic types type Box1 = { value: T };