diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2eeea77e49268..e7294d3941a8e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9686,26 +9686,15 @@ namespace ts { // If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we - // construct the type Box. We do not further simplify the result because mapped types can be recursive - // and we might never terminate. + // construct the type Box. if (isGenericMappedType(objectType)) { - return type.simplified = substituteIndexedMappedType(objectType, type); - } - if (objectType.flags & TypeFlags.TypeParameter) { - const constraint = getConstraintOfTypeParameter(objectType as TypeParameter); - if (constraint && isGenericMappedType(constraint)) { - return type.simplified = substituteIndexedMappedType(constraint, type); - } + const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [type.indexType]); + const templateMapper = combineTypeMappers(objectType.mapper, mapper); + return type.simplified = mapType(instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper), getSimplifiedType); } return type.simplified = type; } - function substituteIndexedMappedType(objectType: MappedType, type: IndexedAccessType) { - const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [type.indexType]); - const templateMapper = combineTypeMappers(objectType.mapper, mapper); - return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper); - } - function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName, missingType = accessNode ? errorType : unknownType): Type { if (objectType === wildcardType || indexType === wildcardType) { return wildcardType; diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index b7d794285fae1..793313fdaa535 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -484,10 +484,10 @@ function onChangeGenericFunction(handler: Handler) { function updateIds, K extends string>( obj: T, idFields: K[], - idMapping: { [oldId: string]: string } + idMapping: Partial> ): Record { for (const idField of idFields) { - const newId = idMapping[obj[idField]]; + const newId: T[K] | undefined = idMapping[obj[idField]]; if (newId) { obj[idField] = newId; } @@ -1312,9 +1312,7 @@ declare type Handler = { declare function onChangeGenericFunction(handler: Handler): void; -declare function updateIds, K extends string>(obj: T, idFields: K[], idMapping: { - [oldId: string]: string; -}): Record; +declare function updateIds, K extends string>(obj: T, idFields: K[], idMapping: Partial>): Record; declare function updateIds2(obj: T, key: K, stringMap: { diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index 9881365b470ac..1d26f6dbec3a7 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -1767,9 +1767,14 @@ function updateIds, K extends string>( >idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 483, 11)) >K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 482, 47)) - idMapping: { [oldId: string]: string } + idMapping: Partial> >idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 484, 18)) ->oldId : Symbol(oldId, Decl(keyofAndIndexedAccess.ts, 485, 18)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 482, 19)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 482, 47)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 482, 19)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 482, 47)) ): Record { >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) @@ -1779,8 +1784,10 @@ function updateIds, K extends string>( >idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 487, 14)) >idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 483, 11)) - const newId = idMapping[obj[idField]]; + const newId: T[K] | undefined = idMapping[obj[idField]]; >newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 488, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 482, 19)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 482, 47)) >idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 484, 18)) >obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 482, 66)) >idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 487, 14)) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index f4c47b26df0c4..bb395c562d6ef 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -1731,7 +1731,7 @@ function onChangeGenericFunction(handler: Handler) { // Repro from #13285 function updateIds, K extends string>( ->updateIds : , K extends string>(obj: T, idFields: K[], idMapping: { [oldId: string]: string; }) => Record +>updateIds : , K extends string>(obj: T, idFields: K[], idMapping: Partial>) => Record obj: T, >obj : T @@ -1739,32 +1739,31 @@ function updateIds, K extends string>( idFields: K[], >idFields : K[] - idMapping: { [oldId: string]: string } ->idMapping : { [oldId: string]: string; } ->oldId : string + idMapping: Partial> +>idMapping : Partial> ): Record { for (const idField of idFields) { >idField : K >idFields : K[] - const newId = idMapping[obj[idField]]; ->newId : { [oldId: string]: string; }[T[K]] ->idMapping[obj[idField]] : { [oldId: string]: string; }[T[K]] ->idMapping : { [oldId: string]: string; } + const newId: T[K] | undefined = idMapping[obj[idField]]; +>newId : T[K] | undefined +>idMapping[obj[idField]] : Partial>[T[K]] +>idMapping : Partial> >obj[idField] : T[K] >obj : T >idField : K if (newId) { ->newId : { [oldId: string]: string; }[T[K]] +>newId : T[K] | undefined obj[idField] = newId; ->obj[idField] = newId : { [oldId: string]: string; }[T[K]] +>obj[idField] = newId : T[K] >obj[idField] : T[K] >obj : T >idField : K ->newId : { [oldId: string]: string; }[T[K]] +>newId : T[K] } } return obj; diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index cba7ab5246925..b0eaac39c6d21 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -321,4 +321,14 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(142,5): error ~~~~ !!! error TS2322: Type 'number[]' is not assignable to type 'T[K]'. } + + // Repro from #28839 + + function f30() { + let x: Partial>[K] = "hello"; + } + + function f31() { + let x: Partial>>>>>>>[K] = "hello"; + } \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.js b/tests/baselines/reference/keyofAndIndexedAccessErrors.js index 88a1b74f14d39..f262f9a0d3c67 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.js +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.js @@ -142,6 +142,16 @@ function test1, K extends keyof T>(t: T, k: K) { t[k] = "hello"; // Error t[k] = [10, 20]; // Error } + +// Repro from #28839 + +function f30() { + let x: Partial>[K] = "hello"; +} + +function f31() { + let x: Partial>>>>>>>[K] = "hello"; +} //// [keyofAndIndexedAccessErrors.js] @@ -215,3 +225,10 @@ function test1(t, k) { t[k] = "hello"; // Error t[k] = [10, 20]; // Error } +// Repro from #28839 +function f30() { + var x = "hello"; +} +function f31() { + var x = "hello"; +} diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols b/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols index df1707849cb8a..1dd7c0d564828 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols @@ -486,3 +486,39 @@ function test1, K extends keyof T>(t: T, k: K) { >k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 138, 70)) } +// Repro from #28839 + +function f30() { +>f30 : Symbol(f30, Decl(keyofAndIndexedAccessErrors.ts, 142, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 146, 13)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 146, 15)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 146, 13)) + + let x: Partial>[K] = "hello"; +>x : Symbol(x, Decl(keyofAndIndexedAccessErrors.ts, 147, 7)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 146, 13)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 146, 15)) +} + +function f31() { +>f31 : Symbol(f31, Decl(keyofAndIndexedAccessErrors.ts, 148, 1)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 150, 13)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 150, 15)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 150, 13)) + + let x: Partial>>>>>>>[K] = "hello"; +>x : Symbol(x, Decl(keyofAndIndexedAccessErrors.ts, 151, 7)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 150, 13)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 150, 15)) +} + diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.types b/tests/baselines/reference/keyofAndIndexedAccessErrors.types index 68dc361c99ee3..90df0e687a057 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.types +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.types @@ -465,3 +465,21 @@ function test1, K extends keyof T>(t: T, k: K) { >20 : 20 } +// Repro from #28839 + +function f30() { +>f30 : () => void + + let x: Partial>[K] = "hello"; +>x : Partial>[K] +>"hello" : "hello" +} + +function f31() { +>f31 : () => void + + let x: Partial>>>>>>>[K] = "hello"; +>x : Partial>>>>>>>[K] +>"hello" : "hello" +} + diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index cf05bdfed8130..b498310f13be5 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -486,10 +486,10 @@ function onChangeGenericFunction(handler: Handler) { function updateIds, K extends string>( obj: T, idFields: K[], - idMapping: { [oldId: string]: string } + idMapping: Partial> ): Record { for (const idField of idFields) { - const newId = idMapping[obj[idField]]; + const newId: T[K] | undefined = idMapping[obj[idField]]; if (newId) { obj[idField] = newId; } diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts index a4ed608d8e71c..d5fe7c24b5c66 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts @@ -141,3 +141,13 @@ function test1, K extends keyof T>(t: T, k: K) { t[k] = "hello"; // Error t[k] = [10, 20]; // Error } + +// Repro from #28839 + +function f30() { + let x: Partial>[K] = "hello"; +} + +function f31() { + let x: Partial>>>>>>>[K] = "hello"; +}