mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-10 06:41:59 -06:00
Merge pull request #29740 from Microsoft/fixCircularMappedArrayTuple
Fix issues related to circular mapped array and tuple types
This commit is contained in:
commit
d9ee86723a
@ -10854,7 +10854,7 @@ namespace ts {
|
||||
function getHomomorphicTypeVariable(type: MappedType) {
|
||||
const constraintType = getConstraintTypeFromMappedType(type);
|
||||
if (constraintType.flags & TypeFlags.Index) {
|
||||
const typeVariable = (<IndexType>constraintType).type;
|
||||
const typeVariable = getActualTypeVariable((<IndexType>constraintType).type);
|
||||
if (typeVariable.flags & TypeFlags.TypeParameter) {
|
||||
return <TypeParameter>typeVariable;
|
||||
}
|
||||
@ -10877,26 +10877,15 @@ namespace ts {
|
||||
if (typeVariable) {
|
||||
const mappedTypeVariable = instantiateType(typeVariable, mapper);
|
||||
if (typeVariable !== mappedTypeVariable) {
|
||||
// If we are already in the process of creating an instantiation of this mapped type,
|
||||
// return the error type. This situation only arises if we are instantiating the mapped
|
||||
// type for an array or tuple type, as we then need to eagerly resolve the (possibly
|
||||
// circular) element type(s).
|
||||
if (type.instantiating) {
|
||||
return errorType;
|
||||
}
|
||||
type.instantiating = true;
|
||||
const modifiers = getMappedTypeModifiers(type);
|
||||
const result = mapType(mappedTypeVariable, t => {
|
||||
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType) {
|
||||
return mapType(mappedTypeVariable, t => {
|
||||
if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && t !== errorType) {
|
||||
const replacementMapper = createReplacementMapper(typeVariable, t, mapper);
|
||||
return isArrayType(t) ? createArrayType(instantiateMappedTypeTemplate(type, numberType, /*isOptional*/ true, replacementMapper), getModifiedReadonlyState(isReadonlyArrayType(t), modifiers)) :
|
||||
return isArrayType(t) ? instantiateMappedArrayType(t, type, replacementMapper) :
|
||||
isTupleType(t) ? instantiateMappedTupleType(t, type, replacementMapper) :
|
||||
instantiateAnonymousType(type, replacementMapper);
|
||||
}
|
||||
return t;
|
||||
});
|
||||
type.instantiating = false;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return instantiateAnonymousType(type, mapper);
|
||||
@ -10906,6 +10895,12 @@ namespace ts {
|
||||
return modifiers & MappedTypeModifiers.IncludeReadonly ? true : modifiers & MappedTypeModifiers.ExcludeReadonly ? false : state;
|
||||
}
|
||||
|
||||
function instantiateMappedArrayType(arrayType: Type, mappedType: MappedType, mapper: TypeMapper) {
|
||||
const elementType = instantiateMappedTypeTemplate(mappedType, numberType, /*isOptional*/ true, mapper);
|
||||
return elementType === errorType ? errorType :
|
||||
createArrayType(elementType, getModifiedReadonlyState(isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType)));
|
||||
}
|
||||
|
||||
function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) {
|
||||
const minLength = tupleType.target.minLength;
|
||||
const elementTypes = map(tupleType.typeArguments || emptyArray, (_, i) =>
|
||||
@ -10915,7 +10910,8 @@ namespace ts {
|
||||
modifiers & MappedTypeModifiers.ExcludeOptional ? getTypeReferenceArity(tupleType) - (tupleType.target.hasRestElement ? 1 : 0) :
|
||||
minLength;
|
||||
const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
|
||||
return createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
|
||||
return contains(elementTypes, errorType) ? errorType :
|
||||
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
|
||||
}
|
||||
|
||||
function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
|
||||
|
||||
@ -4114,7 +4114,6 @@ namespace ts {
|
||||
templateType?: Type;
|
||||
modifiersType?: Type;
|
||||
resolvedApparentType?: Type;
|
||||
instantiating?: boolean;
|
||||
}
|
||||
|
||||
export interface EvolvingArrayType extends ObjectType {
|
||||
|
||||
@ -4,9 +4,10 @@ tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(7,6): error TS2456:
|
||||
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(8,11): error TS2313: Type parameter 'K' has a circular constraint.
|
||||
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(11,6): error TS2456: Type alias 'Recurse2' circularly references itself.
|
||||
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(12,11): error TS2313: Type parameter 'K' has a circular constraint.
|
||||
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(20,19): error TS2589: Type instantiation is excessively deep and possibly infinite.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (6 errors) ====
|
||||
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (7 errors) ====
|
||||
// Recursive mapped types simply appear empty
|
||||
|
||||
type Recurse = {
|
||||
@ -39,6 +40,47 @@ tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(12,11): error TS231
|
||||
type tup = [number, number, number, number];
|
||||
|
||||
function foo(arg: Circular<tup>): tup {
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
|
||||
return arg;
|
||||
}
|
||||
|
||||
// Repro from #29442
|
||||
|
||||
type DeepMap<T extends unknown[], R> = {
|
||||
[K in keyof T]: T[K] extends unknown[] ? DeepMap<T[K], R> : R;
|
||||
};
|
||||
|
||||
type tpl = [string, [string, [string]]];
|
||||
type arr = string[][];
|
||||
|
||||
type t1 = DeepMap<tpl, number>; // [number, [number, [number]]]
|
||||
type t2 = DeepMap<arr, number>; // number[][]
|
||||
|
||||
// Repro from #29577
|
||||
|
||||
type Transform<T> = { [K in keyof T]: Transform<T[K]> };
|
||||
|
||||
interface User {
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
interface Guest {
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
interface Product {
|
||||
users: (User | Guest)[];
|
||||
}
|
||||
|
||||
declare var product: Transform<Product>;
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
|
||||
// Repro from #29702
|
||||
|
||||
type Remap1<T> = { [P in keyof T]: Remap1<T[P]>; };
|
||||
type Remap2<T> = T extends object ? { [P in keyof T]: Remap2<T[P]>; } : T;
|
||||
|
||||
type a = Remap1<string[]>; // string[]
|
||||
type b = Remap2<string[]>; // string[]
|
||||
|
||||
@ -21,6 +21,45 @@ type tup = [number, number, number, number];
|
||||
function foo(arg: Circular<tup>): tup {
|
||||
return arg;
|
||||
}
|
||||
|
||||
// Repro from #29442
|
||||
|
||||
type DeepMap<T extends unknown[], R> = {
|
||||
[K in keyof T]: T[K] extends unknown[] ? DeepMap<T[K], R> : R;
|
||||
};
|
||||
|
||||
type tpl = [string, [string, [string]]];
|
||||
type arr = string[][];
|
||||
|
||||
type t1 = DeepMap<tpl, number>; // [number, [number, [number]]]
|
||||
type t2 = DeepMap<arr, number>; // number[][]
|
||||
|
||||
// Repro from #29577
|
||||
|
||||
type Transform<T> = { [K in keyof T]: Transform<T[K]> };
|
||||
|
||||
interface User {
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
interface Guest {
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
interface Product {
|
||||
users: (User | Guest)[];
|
||||
}
|
||||
|
||||
declare var product: Transform<Product>;
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
|
||||
// Repro from #29702
|
||||
|
||||
type Remap1<T> = { [P in keyof T]: Remap1<T[P]>; };
|
||||
type Remap2<T> = T extends object ? { [P in keyof T]: Remap2<T[P]>; } : T;
|
||||
|
||||
type a = Remap1<string[]>; // string[]
|
||||
type b = Remap2<string[]>; // string[]
|
||||
|
||||
|
||||
//// [recursiveMappedTypes.js]
|
||||
@ -30,6 +69,7 @@ exports.__esModule = true;
|
||||
function foo(arg) {
|
||||
return arg;
|
||||
}
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
|
||||
|
||||
//// [recursiveMappedTypes.d.ts]
|
||||
|
||||
@ -55,3 +55,113 @@ function foo(arg: Circular<tup>): tup {
|
||||
>arg : Symbol(arg, Decl(recursiveMappedTypes.ts, 19, 13))
|
||||
}
|
||||
|
||||
// Repro from #29442
|
||||
|
||||
type DeepMap<T extends unknown[], R> = {
|
||||
>DeepMap : Symbol(DeepMap, Decl(recursiveMappedTypes.ts, 21, 1))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 25, 13))
|
||||
>R : Symbol(R, Decl(recursiveMappedTypes.ts, 25, 33))
|
||||
|
||||
[K in keyof T]: T[K] extends unknown[] ? DeepMap<T[K], R> : R;
|
||||
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 26, 3))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 25, 13))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 25, 13))
|
||||
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 26, 3))
|
||||
>DeepMap : Symbol(DeepMap, Decl(recursiveMappedTypes.ts, 21, 1))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 25, 13))
|
||||
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 26, 3))
|
||||
>R : Symbol(R, Decl(recursiveMappedTypes.ts, 25, 33))
|
||||
>R : Symbol(R, Decl(recursiveMappedTypes.ts, 25, 33))
|
||||
|
||||
};
|
||||
|
||||
type tpl = [string, [string, [string]]];
|
||||
>tpl : Symbol(tpl, Decl(recursiveMappedTypes.ts, 27, 2))
|
||||
|
||||
type arr = string[][];
|
||||
>arr : Symbol(arr, Decl(recursiveMappedTypes.ts, 29, 40))
|
||||
|
||||
type t1 = DeepMap<tpl, number>; // [number, [number, [number]]]
|
||||
>t1 : Symbol(t1, Decl(recursiveMappedTypes.ts, 30, 22))
|
||||
>DeepMap : Symbol(DeepMap, Decl(recursiveMappedTypes.ts, 21, 1))
|
||||
>tpl : Symbol(tpl, Decl(recursiveMappedTypes.ts, 27, 2))
|
||||
|
||||
type t2 = DeepMap<arr, number>; // number[][]
|
||||
>t2 : Symbol(t2, Decl(recursiveMappedTypes.ts, 32, 31))
|
||||
>DeepMap : Symbol(DeepMap, Decl(recursiveMappedTypes.ts, 21, 1))
|
||||
>arr : Symbol(arr, Decl(recursiveMappedTypes.ts, 29, 40))
|
||||
|
||||
// Repro from #29577
|
||||
|
||||
type Transform<T> = { [K in keyof T]: Transform<T[K]> };
|
||||
>Transform : Symbol(Transform, Decl(recursiveMappedTypes.ts, 33, 31))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 37, 15))
|
||||
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 37, 23))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 37, 15))
|
||||
>Transform : Symbol(Transform, Decl(recursiveMappedTypes.ts, 33, 31))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 37, 15))
|
||||
>K : Symbol(K, Decl(recursiveMappedTypes.ts, 37, 23))
|
||||
|
||||
interface User {
|
||||
>User : Symbol(User, Decl(recursiveMappedTypes.ts, 37, 56))
|
||||
|
||||
avatar: string;
|
||||
>avatar : Symbol(User.avatar, Decl(recursiveMappedTypes.ts, 39, 16))
|
||||
}
|
||||
|
||||
interface Guest {
|
||||
>Guest : Symbol(Guest, Decl(recursiveMappedTypes.ts, 41, 1))
|
||||
|
||||
displayName: string;
|
||||
>displayName : Symbol(Guest.displayName, Decl(recursiveMappedTypes.ts, 43, 17))
|
||||
}
|
||||
|
||||
interface Product {
|
||||
>Product : Symbol(Product, Decl(recursiveMappedTypes.ts, 45, 1))
|
||||
|
||||
users: (User | Guest)[];
|
||||
>users : Symbol(Product.users, Decl(recursiveMappedTypes.ts, 47, 19))
|
||||
>User : Symbol(User, Decl(recursiveMappedTypes.ts, 37, 56))
|
||||
>Guest : Symbol(Guest, Decl(recursiveMappedTypes.ts, 41, 1))
|
||||
}
|
||||
|
||||
declare var product: Transform<Product>;
|
||||
>product : Symbol(product, Decl(recursiveMappedTypes.ts, 51, 11))
|
||||
>Transform : Symbol(Transform, Decl(recursiveMappedTypes.ts, 33, 31))
|
||||
>Product : Symbol(Product, Decl(recursiveMappedTypes.ts, 45, 1))
|
||||
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
>product.users : Symbol(users, Decl(recursiveMappedTypes.ts, 47, 19))
|
||||
>product : Symbol(product, Decl(recursiveMappedTypes.ts, 51, 11))
|
||||
>users : Symbol(users, Decl(recursiveMappedTypes.ts, 47, 19))
|
||||
|
||||
// Repro from #29702
|
||||
|
||||
type Remap1<T> = { [P in keyof T]: Remap1<T[P]>; };
|
||||
>Remap1 : Symbol(Remap1, Decl(recursiveMappedTypes.ts, 52, 14))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 56, 12))
|
||||
>P : Symbol(P, Decl(recursiveMappedTypes.ts, 56, 20))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 56, 12))
|
||||
>Remap1 : Symbol(Remap1, Decl(recursiveMappedTypes.ts, 52, 14))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 56, 12))
|
||||
>P : Symbol(P, Decl(recursiveMappedTypes.ts, 56, 20))
|
||||
|
||||
type Remap2<T> = T extends object ? { [P in keyof T]: Remap2<T[P]>; } : T;
|
||||
>Remap2 : Symbol(Remap2, Decl(recursiveMappedTypes.ts, 56, 51))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 57, 12))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 57, 12))
|
||||
>P : Symbol(P, Decl(recursiveMappedTypes.ts, 57, 39))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 57, 12))
|
||||
>Remap2 : Symbol(Remap2, Decl(recursiveMappedTypes.ts, 56, 51))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 57, 12))
|
||||
>P : Symbol(P, Decl(recursiveMappedTypes.ts, 57, 39))
|
||||
>T : Symbol(T, Decl(recursiveMappedTypes.ts, 57, 12))
|
||||
|
||||
type a = Remap1<string[]>; // string[]
|
||||
>a : Symbol(a, Decl(recursiveMappedTypes.ts, 57, 74))
|
||||
>Remap1 : Symbol(Remap1, Decl(recursiveMappedTypes.ts, 52, 14))
|
||||
|
||||
type b = Remap2<string[]>; // string[]
|
||||
>b : Symbol(b, Decl(recursiveMappedTypes.ts, 59, 26))
|
||||
>Remap2 : Symbol(Remap2, Decl(recursiveMappedTypes.ts, 56, 51))
|
||||
|
||||
|
||||
@ -28,10 +28,72 @@ type tup = [number, number, number, number];
|
||||
>tup : [number, number, number, number]
|
||||
|
||||
function foo(arg: Circular<tup>): tup {
|
||||
>foo : (arg: [any, any, any, any]) => [number, number, number, number]
|
||||
>arg : [any, any, any, any]
|
||||
>foo : (arg: any) => [number, number, number, number]
|
||||
>arg : any
|
||||
|
||||
return arg;
|
||||
>arg : [any, any, any, any]
|
||||
>arg : any
|
||||
}
|
||||
|
||||
// Repro from #29442
|
||||
|
||||
type DeepMap<T extends unknown[], R> = {
|
||||
>DeepMap : DeepMap<T, R>
|
||||
|
||||
[K in keyof T]: T[K] extends unknown[] ? DeepMap<T[K], R> : R;
|
||||
};
|
||||
|
||||
type tpl = [string, [string, [string]]];
|
||||
>tpl : [string, [string, [string]]]
|
||||
|
||||
type arr = string[][];
|
||||
>arr : string[][]
|
||||
|
||||
type t1 = DeepMap<tpl, number>; // [number, [number, [number]]]
|
||||
>t1 : [number, [number, [number]]]
|
||||
|
||||
type t2 = DeepMap<arr, number>; // number[][]
|
||||
>t2 : number[][]
|
||||
|
||||
// Repro from #29577
|
||||
|
||||
type Transform<T> = { [K in keyof T]: Transform<T[K]> };
|
||||
>Transform : Transform<T>
|
||||
|
||||
interface User {
|
||||
avatar: string;
|
||||
>avatar : string
|
||||
}
|
||||
|
||||
interface Guest {
|
||||
displayName: string;
|
||||
>displayName : string
|
||||
}
|
||||
|
||||
interface Product {
|
||||
users: (User | Guest)[];
|
||||
>users : (User | Guest)[]
|
||||
}
|
||||
|
||||
declare var product: Transform<Product>;
|
||||
>product : Transform<Product>
|
||||
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
>product.users : (Transform<User> | Transform<Guest>)[]
|
||||
>product : Transform<Product>
|
||||
>users : (Transform<User> | Transform<Guest>)[]
|
||||
|
||||
// Repro from #29702
|
||||
|
||||
type Remap1<T> = { [P in keyof T]: Remap1<T[P]>; };
|
||||
>Remap1 : Remap1<T>
|
||||
|
||||
type Remap2<T> = T extends object ? { [P in keyof T]: Remap2<T[P]>; } : T;
|
||||
>Remap2 : Remap2<T>
|
||||
|
||||
type a = Remap1<string[]>; // string[]
|
||||
>a : string[]
|
||||
|
||||
type b = Remap2<string[]>; // string[]
|
||||
>b : string[]
|
||||
|
||||
|
||||
@ -22,3 +22,42 @@ type tup = [number, number, number, number];
|
||||
function foo(arg: Circular<tup>): tup {
|
||||
return arg;
|
||||
}
|
||||
|
||||
// Repro from #29442
|
||||
|
||||
type DeepMap<T extends unknown[], R> = {
|
||||
[K in keyof T]: T[K] extends unknown[] ? DeepMap<T[K], R> : R;
|
||||
};
|
||||
|
||||
type tpl = [string, [string, [string]]];
|
||||
type arr = string[][];
|
||||
|
||||
type t1 = DeepMap<tpl, number>; // [number, [number, [number]]]
|
||||
type t2 = DeepMap<arr, number>; // number[][]
|
||||
|
||||
// Repro from #29577
|
||||
|
||||
type Transform<T> = { [K in keyof T]: Transform<T[K]> };
|
||||
|
||||
interface User {
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
interface Guest {
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
interface Product {
|
||||
users: (User | Guest)[];
|
||||
}
|
||||
|
||||
declare var product: Transform<Product>;
|
||||
product.users; // (Transform<User> | Transform<Guest>)[]
|
||||
|
||||
// Repro from #29702
|
||||
|
||||
type Remap1<T> = { [P in keyof T]: Remap1<T[P]>; };
|
||||
type Remap2<T> = T extends object ? { [P in keyof T]: Remap2<T[P]>; } : T;
|
||||
|
||||
type a = Remap1<string[]>; // string[]
|
||||
type b = Remap2<string[]>; // string[]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user