mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
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:
parent
2426eb4980
commit
cd30534327
@ -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
|
||||
|
||||
@ -5290,8 +5290,6 @@ namespace ts {
|
||||
node: ConditionalTypeNode;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
isDistributive: boolean;
|
||||
inferTypeParameters?: TypeParameter[];
|
||||
outerTypeParameters?: TypeParameter[];
|
||||
|
||||
@ -2605,8 +2605,6 @@ declare namespace ts {
|
||||
node: ConditionalTypeNode;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
isDistributive: boolean;
|
||||
inferTypeParameters?: TypeParameter[];
|
||||
outerTypeParameters?: TypeParameter[];
|
||||
|
||||
@ -2605,8 +2605,6 @@ declare namespace ts {
|
||||
node: ConditionalTypeNode;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
isDistributive: boolean;
|
||||
inferTypeParameters?: TypeParameter[];
|
||||
outerTypeParameters?: TypeParameter[];
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
195
tests/baselines/reference/recursiveConditionalTypes.errors.txt
Normal file
195
tests/baselines/reference/recursiveConditionalTypes.errors.txt
Normal 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'.
|
||||
}
|
||||
|
||||
216
tests/baselines/reference/recursiveConditionalTypes.js
Normal file
216
tests/baselines/reference/recursiveConditionalTypes.js
Normal 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;
|
||||
467
tests/baselines/reference/recursiveConditionalTypes.symbols
Normal file
467
tests/baselines/reference/recursiveConditionalTypes.symbols
Normal 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))
|
||||
}
|
||||
|
||||
312
tests/baselines/reference/recursiveConditionalTypes.types
Normal file
312
tests/baselines/reference/recursiveConditionalTypes.types
Normal 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>
|
||||
}
|
||||
|
||||
121
tests/cases/compiler/recursiveConditionalTypes.ts
Normal file
121
tests/cases/compiler/recursiveConditionalTypes.ts
Normal 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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user