Merge pull request #29740 from Microsoft/fixCircularMappedArrayTuple

Fix issues related to circular mapped array and tuple types
This commit is contained in:
Anders Hejlsberg 2019-02-06 13:24:19 -08:00 committed by GitHub
commit d9ee86723a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 309 additions and 21 deletions

View File

@ -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) {

View File

@ -4114,7 +4114,6 @@ namespace ts {
templateType?: Type;
modifiersType?: Type;
resolvedApparentType?: Type;
instantiating?: boolean;
}
export interface EvolvingArrayType extends ObjectType {

View File

@ -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[]

View File

@ -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]

View File

@ -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))

View File

@ -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[]

View File

@ -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[]