Recursive conditional types (#40002)

* Support recursive conditional types

* Accept new API baselines

* Accept new baselines

* Simplify recursive type tracking in type inference

* Accept new baselines

* Add tests

* Accept new baselines

* Revise recursion tracking in type inference

* Revise tests

* Accept new baselines

* Add more tests

* Accept new baselines
This commit is contained in:
Anders Hejlsberg 2020-08-15 18:22:30 -07:00 committed by GitHub
parent 2426eb4980
commit cd30534327
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1392 additions and 116 deletions

View File

@ -13810,11 +13810,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 = (<ConditionalType>falseType).root;
if (newRoot.node.parent === root.node && (!newRoot.isDistributive || newRoot.checkType === root.checkType)) {
@ -13822,7 +13822,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
@ -13831,7 +13831,7 @@ namespace ts {
// type Foo<T extends { x: any }> = 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;
}
}
@ -13851,15 +13851,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 {
@ -13886,8 +13886,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,
@ -14878,17 +14876,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) {
@ -18200,56 +18187,53 @@ namespace ts {
// has expanded into `[A<NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>>>>>>]`
// 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;
// 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 (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 (isTupleType(type)) {
// Tuple types are tracked through their target type
return type.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 {
@ -19154,9 +19138,7 @@ namespace ts {
function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean {
return !!(type === typeParameter ||
type.flags & TypeFlags.UnionOrIntersection && some((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter)) ||
type.flags & TypeFlags.Conditional && (
isTypeParameterAtTopLevel(getTrueTypeFromConditionalType(<ConditionalType>type), typeParameter) ||
isTypeParameterAtTopLevel(getFalseTypeFromConditionalType(<ConditionalType>type), typeParameter)));
type.flags & TypeFlags.Conditional && (getTrueTypeFromConditionalType(<ConditionalType>type) === typeParameter || getFalseTypeFromConditionalType(<ConditionalType>type) === typeParameter));
}
/** Create an object with properties named in the string literal type. Every property has type `any` */
@ -19306,14 +19288,14 @@ namespace ts {
}
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
let symbolOrTypeStack: (Symbol | Type)[];
let visited: ESMap<string, number>;
let bivariant = false;
let propagationType: Type;
let inferencePriority = InferencePriority.MaxValue;
let allowComplexConstraintInference = true;
let objectTypeComparisonDepth = 0;
const targetStack: Type[] = [];
let visited: ESMap<string, number>;
let sourceStack: object[];
let targetStack: object[];
let expandingFlags = ExpandingFlags.None;
inferFromTypes(originalSource, originalTarget);
function inferFromTypes(source: Type, target: Type): void {
@ -19472,18 +19454,8 @@ namespace ts {
inferFromTypes((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType);
inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType);
}
else if (source.flags & TypeFlags.Conditional && target.flags & TypeFlags.Conditional) {
inferFromTypes((<ConditionalType>source).checkType, (<ConditionalType>target).checkType);
inferFromTypes((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType);
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
}
else if (target.flags & TypeFlags.Conditional) {
const savePriority = priority;
priority |= contravariant ? InferencePriority.ContravariantConditional : 0;
const targetTypes = [getTrueTypeFromConditionalType(<ConditionalType>target), getFalseTypeFromConditionalType(<ConditionalType>target)];
inferToMultipleTypes(source, targetTypes, target.flags);
priority = savePriority;
invokeOnce(source, target, inferToConditionalType);
}
else if (target.flags & TypeFlags.UnionOrIntersection) {
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
@ -19544,7 +19516,24 @@ namespace ts {
(visited || (visited = new Map<string, number>())).set(key, InferencePriority.Circularity);
const saveInferencePriority = inferencePriority;
inferencePriority = InferencePriority.MaxValue;
action(source, target);
// 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;
}
expandingFlags = saveExpandingFlags;
visited.set(key, inferencePriority);
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
}
@ -19740,41 +19729,23 @@ namespace ts {
return false;
}
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)) {
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;
}
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);
symbolOrTypeStack.pop();
objectTypeComparisonDepth--;
function inferToConditionalType(source: Type, target: ConditionalType) {
if (source.flags & TypeFlags.Conditional) {
inferFromTypes((<ConditionalType>source).checkType, target.checkType);
inferFromTypes((<ConditionalType>source).extendsType, target.extendsType);
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(target));
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(target));
}
else {
inferFromObjectTypesWorker(source, target);
const savePriority = priority;
priority |= contravariant ? InferencePriority.ContravariantConditional : 0;
const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)];
inferToMultipleTypes(source, targetTypes, target.flags);
priority = savePriority;
}
}
function inferFromObjectTypesWorker(source: Type, target: Type) {
function inferFromObjectTypes(source: Type, target: Type) {
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
// If source and target are references to the same generic type, infer from type arguments

View File

@ -5290,8 +5290,6 @@ namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];

View File

@ -2605,8 +2605,6 @@ declare namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];

View File

@ -2605,8 +2605,6 @@ declare namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];

View File

@ -108,5 +108,5 @@ type T0 = Foo<99>;
>T0 : "99"
type T1 = Foo<any>;
>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"

View File

@ -22,8 +22,8 @@ declare function convert(s: string): IPromise<number>;
>s : string
var $$x = load("something").then(s => convert(s));
>$$x : CPromise<unknown>
>load("something").then(s => convert(s)) : CPromise<unknown>
>$$x : CPromise<number>
>load("something").then(s => convert(s)) : CPromise<number>
>load("something").then : <U>(success?: (value: string) => CPromise<U>) => CPromise<U>
>load("something") : CPromise<string>
>load : (name: string) => CPromise<string>

View File

@ -0,0 +1,195 @@
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<T>' is not assignable to type 'Awaited<U>'.
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<T>'.
tests/cases/compiler/recursiveConditionalTypes.ts(22,5): error TS2322: Type 'Awaited<T>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited<T>'.
Type 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)'.
Type 'T extends PromiseLike<infer U> ? Awaited<U> : T' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'T extends PromiseLike<infer U> ? Awaited<U> : 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<number, M>' is not assignable to type 'TupleOf<number, N>'.
Type 'number extends M ? number[] : _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[] | _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[]' is not assignable to type 'TupleOf<number, N>'.
tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
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>'.
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> =
T extends null | undefined ? T :
T extends PromiseLike<infer U> ? Awaited<U> :
T;
type MyPromise<T> = {
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
}
type InfinitePromise<T> = Promise<InfinitePromise<T>>;
type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
type P1 = Awaited<any>;
type P2 = Awaited<InfinitePromise<number>>; // Error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
ta = ua;
ua = ta; // Error
~~
!!! error TS2322: Type 'Awaited<T>' is not assignable to type 'Awaited<U>'.
!!! 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<T>'.
tx = ta; // Error
~~
!!! error TS2322: Type 'Awaited<T>' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited<T>'.
!!! error TS2322: Type 'T | (T extends PromiseLike<infer U> ? Awaited<U> : 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<infer U> ? Awaited<U> : T)'.
!!! error TS2322: Type 'T extends PromiseLike<infer U> ? Awaited<U> : 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<infer U> ? Awaited<U> : 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 readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
type InfiniteArray<T> = InfiniteArray<T>[];
type B0 = Flatten<string[][][]>;
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
type B2 = Flatten<InfiniteArray<string>>;
type B3 = B2[0]; // Error
~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
// Repeating tuples
type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
type TT0 = TupleOf<string, 4>;
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
tn = tm;
~~
!!! error TS2322: Type 'TupleOf<number, M>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number extends M ? number[] : _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number[] | _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf<number, N>'.
tm = tn;
~~
!!! error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
}
declare function f23<T>(t: TupleOf<T, 3>): T;
f23(['a', 'b', 'c']); // string
// Inference to recursive type
interface Box<T> { value: T };
type RecBox<T> = T | Box<RecBox<T>>;
type InfBox<T> = Box<InfBox<T>>;
declare function unbox<T>(box: RecBox<T>): T
type T1 = Box<string>;
type T2 = Box<T1>;
type T3 = Box<T2>;
type T4 = Box<T3>;
type T5 = Box<T4>;
type T6 = Box<T5>;
declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
declare let b2: T6;
declare let b3: InfBox<string>;
declare let b4: { value: { value: { value: typeof b4 }}};
unbox(b1); // string
unbox(b2); // string
unbox(b3); // InfBox<string>
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<T> = { value: T };
type Box2<T> = { value: T };
declare function foo<T>(x: Box1<Box1<T>>): T;
declare let z: Box2<Box2<string>>;
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
// Intersect tuple element types
type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
type QQ = Intersect<[string[], number[], 7]>;
// Infer between structurally identical recursive conditional types
type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;
function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
x = y;
y = x;
f20(y, x);
}
type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
function f21<T extends number>(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'.
}

View File

@ -0,0 +1,216 @@
//// [recursiveConditionalTypes.ts]
// Awaiting promises
type Awaited<T> =
T extends null | undefined ? T :
T extends PromiseLike<infer U> ? Awaited<U> :
T;
type MyPromise<T> = {
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
}
type InfinitePromise<T> = Promise<InfinitePromise<T>>;
type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
type P1 = Awaited<any>;
type P2 = Awaited<InfinitePromise<number>>; // Error
function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
ta = ua;
ua = ta; // Error
ta = tx; // Error
tx = ta; // Error
}
// Flattening arrays
type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
type InfiniteArray<T> = InfiniteArray<T>[];
type B0 = Flatten<string[][][]>;
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
type B2 = Flatten<InfiniteArray<string>>;
type B3 = B2[0]; // Error
// Repeating tuples
type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
type TT0 = TupleOf<string, 4>;
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
tn = tm;
tm = tn;
}
declare function f23<T>(t: TupleOf<T, 3>): T;
f23(['a', 'b', 'c']); // string
// Inference to recursive type
interface Box<T> { value: T };
type RecBox<T> = T | Box<RecBox<T>>;
type InfBox<T> = Box<InfBox<T>>;
declare function unbox<T>(box: RecBox<T>): T
type T1 = Box<string>;
type T2 = Box<T1>;
type T3 = Box<T2>;
type T4 = Box<T3>;
type T5 = Box<T4>;
type T6 = Box<T5>;
declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
declare let b2: T6;
declare let b3: InfBox<string>;
declare let b4: { value: { value: { value: typeof b4 }}};
unbox(b1); // string
unbox(b2); // string
unbox(b3); // InfBox<string>
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<T> = { value: T };
type Box2<T> = { value: T };
declare function foo<T>(x: Box1<Box1<T>>): T;
declare let z: Box2<Box2<string>>;
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
// Intersect tuple element types
type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
type QQ = Intersect<[string[], number[], 7]>;
// Infer between structurally identical recursive conditional types
type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;
function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
x = y;
y = x;
f20(y, x);
}
type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
function f21<T extends number>(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
;
unbox(b1); // string
unbox(b2); // string
unbox(b3); // InfBox<string>
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;
y = x;
f20(y, x);
}
function f21(x, y) {
f21(y, x); // Error
}
//// [recursiveConditionalTypes.d.ts]
declare type Awaited<T> = T extends null | undefined ? T : T extends PromiseLike<infer U> ? Awaited<U> : T;
declare type MyPromise<T> = {
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
};
declare type InfinitePromise<T> = Promise<InfinitePromise<T>>;
declare type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
declare type P1 = Awaited<any>;
declare type P2 = Awaited<InfinitePromise<number>>;
declare function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>): void;
declare type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
declare type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
declare type InfiniteArray<T> = InfiniteArray<T>[];
declare type B0 = Flatten<string[][][]>;
declare type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
declare type B2 = Flatten<InfiniteArray<string>>;
declare type B3 = B2[0];
declare type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
declare type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
declare type TT0 = TupleOf<string, 4>;
declare type TT1 = TupleOf<number, 0 | 2 | 4>;
declare type TT2 = TupleOf<number, number>;
declare type TT3 = TupleOf<number, any>;
declare type TT4 = TupleOf<number, 100>;
declare function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>): void;
declare function f23<T>(t: TupleOf<T, 3>): T;
interface Box<T> {
value: T;
}
declare type RecBox<T> = T | Box<RecBox<T>>;
declare type InfBox<T> = Box<InfBox<T>>;
declare function unbox<T>(box: RecBox<T>): T;
declare type T1 = Box<string>;
declare type T2 = Box<T1>;
declare type T3 = Box<T2>;
declare type T4 = Box<T3>;
declare type T5 = Box<T4>;
declare type T6 = Box<T5>;
declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
declare let b2: T6;
declare let b3: InfBox<string>;
declare let b4: {
value: {
value: {
value: typeof b4;
};
};
};
declare type Box1<T> = {
value: T;
};
declare type Box2<T> = {
value: T;
};
declare function foo<T>(x: Box1<Box1<T>>): T;
declare let z: Box2<Box2<string>>;
declare type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
declare type QQ = Intersect<[string[], number[], 7]>;
declare type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
declare type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;
declare function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>): void;
declare type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
declare type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
declare function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>): void;

View File

@ -0,0 +1,467 @@
=== tests/cases/compiler/recursiveConditionalTypes.ts ===
// Awaiting promises
type Awaited<T> =
>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<infer U> ? Awaited<U> :
>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<T> = {
>MyPromise : Symbol(MyPromise, Decl(recursiveConditionalTypes.ts, 5, 6))
>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 7, 15))
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
>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))
>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))
}
type InfinitePromise<T> = Promise<InfinitePromise<T>>;
>InfinitePromise : Symbol(InfinitePromise, Decl(recursiveConditionalTypes.ts, 9, 1))
>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 11, 21))
>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<Promise<string | Promise<MyPromise<number> | 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, --, --), 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<any>;
>P1 : Symbol(P1, Decl(recursiveConditionalTypes.ts, 13, 83))
>Awaited : Symbol(Awaited, Decl(recursiveConditionalTypes.ts, 0, 0))
type P2 = Awaited<InfinitePromise<number>>; // 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<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
>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 readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
>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> = T extends readonly (infer U)[] ? _Flatten<U> : 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<T> = InfiniteArray<T>[];
>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<string[][][]>;
>B0 : Symbol(B0, Decl(recursiveConditionalTypes.ts, 29, 43))
>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1))
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
>B1 : Symbol(B1, Decl(recursiveConditionalTypes.ts, 31, 32))
>Flatten : Symbol(Flatten, Decl(recursiveConditionalTypes.ts, 22, 1))
type B2 = Flatten<InfiniteArray<string>>;
>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<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : 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<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
>_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<string, 4>;
>TT0 : Symbol(TT0, Decl(recursiveConditionalTypes.ts, 39, 112))
>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16))
type TT1 = TupleOf<number, 0 | 2 | 4>;
>TT1 : Symbol(TT1, Decl(recursiveConditionalTypes.ts, 41, 30))
>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16))
type TT2 = TupleOf<number, number>;
>TT2 : Symbol(TT2, Decl(recursiveConditionalTypes.ts, 42, 38))
>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16))
type TT3 = TupleOf<number, any>;
>TT3 : Symbol(TT3, Decl(recursiveConditionalTypes.ts, 43, 35))
>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16))
type TT4 = TupleOf<number, 100>; // Depth error
>TT4 : Symbol(TT4, Decl(recursiveConditionalTypes.ts, 44, 32))
>TupleOf : Symbol(TupleOf, Decl(recursiveConditionalTypes.ts, 34, 16))
function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
>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>(t: TupleOf<T, 3>): 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 to recursive type
interface Box<T> { 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> = T | Box<RecBox<T>>;
>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<T> = Box<InfBox<T>>;
>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<T>(box: RecBox<T>): 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<string>;
>T1 : Symbol(T1, Decl(recursiveConditionalTypes.ts, 62, 44))
>Box : Symbol(Box, Decl(recursiveConditionalTypes.ts, 54, 21))
type T2 = Box<T1>;
>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<T2>;
>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<T3>;
>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<T4>;
>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<T5>;
>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<Box<Box<Box<Box<Box<string>>>>>>;
>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<string>;
>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<string>
>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<T> = { value: T };
>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<T> = { value: T };
>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<T>(x: Box1<Box1<T>>): T;
>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<Box2<string>>;
>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, 86, 28))
>z : Symbol(z, Decl(recursiveConditionalTypes.ts, 90, 11))
// Intersect tuple element types
type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
>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, 96, 105))
>Intersect : Symbol(Intersect, Decl(recursiveConditionalTypes.ts, 92, 7))
// Infer between structurally identical recursive conditional types
type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
>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> = T extends (infer U)[] ? Unpack2<U> : T;
>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<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
>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, 105, 29))
>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43))
y = x;
>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, 103, 57))
>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 105, 43))
>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 105, 29))
}
type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
>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 extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
>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<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
>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, 112, 105))
>y : Symbol(y, Decl(recursiveConditionalTypes.ts, 114, 47))
>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 114, 31))
}

View File

@ -0,0 +1,312 @@
=== tests/cases/compiler/recursiveConditionalTypes.ts ===
// Awaiting promises
type Awaited<T> =
>Awaited : Awaited<T>
T extends null | undefined ? T :
>null : null
T extends PromiseLike<infer U> ? Awaited<U> :
T;
type MyPromise<T> = {
>MyPromise : MyPromise<T>
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
>then : <U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined) => MyPromise<U>
>f : ((value: T) => U | PromiseLike<U>) | null | undefined
>value : T
>null : null
}
type InfinitePromise<T> = Promise<InfinitePromise<T>>;
>InfinitePromise : InfinitePromise<T>
type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
>P0 : string | number | null | undefined
>null : null
type P1 = Awaited<any>;
>P1 : any
type P2 = Awaited<InfinitePromise<number>>; // Error
>P2 : any
function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
>f11 : <T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) => void
>tx : T
>ta : Awaited<T>
>ux : U
>ua : Awaited<U>
ta = ua;
>ta = ua : Awaited<U>
>ta : Awaited<T>
>ua : Awaited<U>
ua = ta; // Error
>ua = ta : Awaited<T>
>ua : Awaited<U>
>ta : Awaited<T>
ta = tx; // Error
>ta = tx : T
>ta : Awaited<T>
>tx : T
tx = ta; // Error
>tx = ta : Awaited<T>
>tx : T
>ta : Awaited<T>
}
// Flattening arrays
type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
>Flatten : Flatten<T>
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
>_Flatten : _Flatten<T>
type InfiniteArray<T> = InfiniteArray<T>[];
>InfiniteArray : InfiniteArray<T>
type B0 = Flatten<string[][][]>;
>B0 : string[]
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
>B1 : string[] | readonly (number | boolean)[]
type B2 = Flatten<InfiniteArray<string>>;
>B2 : any[]
type B3 = B2[0]; // Error
>B3 : any
// Repeating tuples
type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
>TupleOf : TupleOf<T, N>
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
>_TupleOf : _TupleOf<T, N, R>
type TT0 = TupleOf<string, 4>;
>TT0 : [string, string, string, string]
type TT1 = TupleOf<number, 0 | 2 | 4>;
>TT1 : [] | [number, number] | [number, number, number, number]
type TT2 = TupleOf<number, number>;
>TT2 : number[]
type TT3 = TupleOf<number, any>;
>TT3 : number[]
type TT4 = TupleOf<number, 100>; // Depth error
>TT4 : any
function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
>f22 : <N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) => void
>tn : TupleOf<number, N>
>tm : TupleOf<number, M>
tn = tm;
>tn = tm : TupleOf<number, M>
>tn : TupleOf<number, N>
>tm : TupleOf<number, M>
tm = tn;
>tm = tn : TupleOf<number, N>
>tm : TupleOf<number, M>
>tn : TupleOf<number, N>
}
declare function f23<T>(t: TupleOf<T, 3>): T;
>f23 : <T>(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]) => T
>['a', 'b', 'c'] : [string, string, string]
>'a' : "a"
>'b' : "b"
>'c' : "c"
// Inference to recursive type
interface Box<T> { value: T };
>value : T
type RecBox<T> = T | Box<RecBox<T>>;
>RecBox : RecBox<T>
type InfBox<T> = Box<InfBox<T>>;
>InfBox : InfBox<T>
declare function unbox<T>(box: RecBox<T>): T
>unbox : <T>(box: RecBox<T>) => T
>box : RecBox<T>
type T1 = Box<string>;
>T1 : T1
type T2 = Box<T1>;
>T2 : T2
type T3 = Box<T2>;
>T3 : T3
type T4 = Box<T3>;
>T4 : T4
type T5 = Box<T4>;
>T5 : T5
type T6 = Box<T5>;
>T6 : T6
declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
>b1 : Box<Box<Box<Box<Box<Box<string>>>>>>
declare let b2: T6;
>b2 : T6
declare let b3: InfBox<string>;
>b3 : InfBox<string>
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 : <T>(box: RecBox<T>) => T
>b1 : Box<Box<Box<Box<Box<Box<string>>>>>>
unbox(b2); // string
>unbox(b2) : string
>unbox : <T>(box: RecBox<T>) => T
>b2 : T6
unbox(b3); // InfBox<string>
>unbox(b3) : InfBox<string>
>unbox : <T>(box: RecBox<T>) => T
>b3 : InfBox<string>
unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number
>unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}) : number
>unbox : <T>(box: RecBox<T>) => 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 : <T>(box: RecBox<T>) => 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 : <T>(box: RecBox<T>) => 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<RecBox<{ readonly value: { readonly value: any; }; }>>
// Inference from nested instantiations of same generic types
type Box1<T> = { value: T };
>Box1 : Box1<T>
>value : T
type Box2<T> = { value: T };
>Box2 : Box2<T>
>value : T
declare function foo<T>(x: Box1<Box1<T>>): T;
>foo : <T>(x: Box1<Box1<T>>) => T
>x : Box1<Box1<T>>
declare let z: Box2<Box2<string>>;
>z : Box2<Box2<string>>
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
>foo(z) : unknown
>foo : <T>(x: Box1<Box1<T>>) => T
>z : Box2<Box2<string>>
// Intersect tuple element types
type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
>Intersect : Intersect<U, R>
type QQ = Intersect<[string[], number[], 7]>;
>QQ : string[] & number[] & 7
// Infer between structurally identical recursive conditional types
type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
>Unpack1 : Unpack1<T>
type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;
>Unpack2 : Unpack2<T>
function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
>f20 : <T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) => void
>x : Unpack1<T>
>y : Unpack2<T>
x = y;
>x = y : Unpack2<T>
>x : Unpack1<T>
>y : Unpack2<T>
y = x;
>y = x : Unpack1<T>
>y : Unpack2<T>
>x : Unpack1<T>
f20(y, x);
>f20(y, x) : void
>f20 : <T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) => void
>y : Unpack2<T>
>x : Unpack1<T>
}
type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
>Grow1 : Grow1<T, N>
type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
>Grow2 : Grow2<T, N>
function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
>f21 : <T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) => void
>x : Grow1<[], T>
>y : Grow2<[], T>
f21(y, x); // Error
>f21(y, x) : void
>f21 : <T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) => void
>y : Grow2<[], T>
>x : Grow1<[], T>
}

View File

@ -0,0 +1,121 @@
// @strict: true
// @declaration: true
// @target: esnext
// Awaiting promises
type Awaited<T> =
T extends null | undefined ? T :
T extends PromiseLike<infer U> ? Awaited<U> :
T;
type MyPromise<T> = {
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
}
type InfinitePromise<T> = Promise<InfinitePromise<T>>;
type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
type P1 = Awaited<any>;
type P2 = Awaited<InfinitePromise<number>>; // Error
function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
ta = ua;
ua = ta; // Error
ta = tx; // Error
tx = ta; // Error
}
// Flattening arrays
type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;
type InfiniteArray<T> = InfiniteArray<T>[];
type B0 = Flatten<string[][][]>;
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
type B2 = Flatten<InfiniteArray<string>>;
type B3 = B2[0]; // Error
// Repeating tuples
type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;
type TT0 = TupleOf<string, 4>;
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
tn = tm;
tm = tn;
}
declare function f23<T>(t: TupleOf<T, 3>): T;
f23(['a', 'b', 'c']); // string
// Inference to recursive type
interface Box<T> { value: T };
type RecBox<T> = T | Box<RecBox<T>>;
type InfBox<T> = Box<InfBox<T>>;
declare function unbox<T>(box: RecBox<T>): T
type T1 = Box<string>;
type T2 = Box<T1>;
type T3 = Box<T2>;
type T4 = Box<T3>;
type T5 = Box<T4>;
type T6 = Box<T5>;
declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
declare let b2: T6;
declare let b3: InfBox<string>;
declare let b4: { value: { value: { value: typeof b4 }}};
unbox(b1); // string
unbox(b2); // string
unbox(b3); // InfBox<string>
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<T> = { value: T };
type Box2<T> = { value: T };
declare function foo<T>(x: Box1<Box1<T>>): T;
declare let z: Box2<Box2<string>>;
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
// Intersect tuple element types
type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;
type QQ = Intersect<[string[], number[], 7]>;
// Infer between structurally identical recursive conditional types
type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;
function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
x = y;
y = x;
f20(y, x);
}
type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
f21(y, x); // Error
}