diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff5d1c661e866..4f2f065f4f315 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10386,7 +10386,7 @@ namespace ts { // results for union and intersection types for performance reasons. function couldContainTypeVariables(type: Type): boolean { const objectFlags = getObjectFlags(type); - return !!(type.flags & TypeFlags.TypeVariable || + return !!(type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) || objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeVariables) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || objectFlags & ObjectFlags.Mapped || @@ -10554,6 +10554,13 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } + else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) { + inferFromTypes((source).type, (target).type); + } + else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { + inferFromTypes((source).objectType, (target).objectType); + inferFromTypes((source).indexType, (target).indexType); + } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (target).types; let typeVariableCount = 0; @@ -10627,6 +10634,12 @@ namespace ts { } function inferFromObjectTypes(source: Type, target: Type) { + if (isGenericMappedType(source) && isGenericMappedType(target)) { + // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer + // from S to T and from X to Y. + inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); + inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); + } if (getObjectFlags(target) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(target); if (constraintType.flags & TypeFlags.Index) { diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.js b/tests/baselines/reference/higherOrderMappedIndexLookupInference.js new file mode 100644 index 0000000000000..e8e09a59713cb --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.js @@ -0,0 +1,43 @@ +//// [higherOrderMappedIndexLookupInference.ts] +// @strict + +function f1(a: () => keyof T, b: () => keyof U) { + a = b; + b = a; +} + +function f2(a: () => T[K], b: () => U[L]) { + a = b; + b = a; +} + +function f3(a: () => { [K in keyof T]: T[K] }, b: () => { [K in keyof U]: U[K] }) { + a = b; + b = a; +} + +// Repro from #18338 + +type IdMapped = { [K in keyof T]: T[K] } + +declare const f: () => IdMapped; +declare const g: () => { [K in keyof U]: U[K] }; + +const h: typeof g = f; + + +//// [higherOrderMappedIndexLookupInference.js] +// @strict +function f1(a, b) { + a = b; + b = a; +} +function f2(a, b) { + a = b; + b = a; +} +function f3(a, b) { + a = b; + b = a; +} +var h = f; diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols b/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols new file mode 100644 index 0000000000000..44ecb1e6d0219 --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols @@ -0,0 +1,98 @@ +=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts === +// @strict + +function f1(a: () => keyof T, b: () => keyof U) { +>f1 : Symbol(f1, Decl(higherOrderMappedIndexLookupInference.ts, 0, 0)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +} + +function f2(a: () => T[K], b: () => U[L]) { +>f2 : Symbol(f2, Decl(higherOrderMappedIndexLookupInference.ts, 5, 1)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +} + +function f3(a: () => { [K in keyof T]: T[K] }, b: () => { [K in keyof U]: U[K] }) { +>f3 : Symbol(f3, Decl(higherOrderMappedIndexLookupInference.ts, 10, 1)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +} + +// Repro from #18338 + +type IdMapped = { [K in keyof T]: T[K] } +>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22)) + +declare const f: () => IdMapped; +>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18)) +>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18)) + +declare const g: () => { [K in keyof U]: U[K] }; +>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29)) + +const h: typeof g = f; +>h : Symbol(h, Decl(higherOrderMappedIndexLookupInference.ts, 24, 5)) +>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13)) +>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13)) + diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.types b/tests/baselines/reference/higherOrderMappedIndexLookupInference.types new file mode 100644 index 0000000000000..f1c2b0aa17ba4 --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.types @@ -0,0 +1,104 @@ +=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts === +// @strict + +function f1(a: () => keyof T, b: () => keyof U) { +>f1 : (a: () => keyof T, b: () => keyof U) => void +>a : () => keyof T +>T : T +>T : T +>b : () => keyof U +>U : U +>U : U + + a = b; +>a = b : () => keyof U +>a : () => keyof T +>b : () => keyof U + + b = a; +>b = a : () => keyof T +>b : () => keyof U +>a : () => keyof T +} + +function f2(a: () => T[K], b: () => U[L]) { +>f2 : (a: () => T[K], b: () => U[L]) => void +>a : () => T[K] +>T : T +>K : K +>T : T +>T : T +>K : K +>b : () => U[L] +>U : U +>L : L +>U : U +>U : U +>L : L + + a = b; +>a = b : () => U[L] +>a : () => T[K] +>b : () => U[L] + + b = a; +>b = a : () => T[K] +>b : () => U[L] +>a : () => T[K] +} + +function f3(a: () => { [K in keyof T]: T[K] }, b: () => { [K in keyof U]: U[K] }) { +>f3 : (a: () => { [K in keyof T]: T[K]; }, b: () => { [K in keyof U]: U[K]; }) => void +>a : () => { [K in keyof T]: T[K]; } +>T : T +>K : K +>T : T +>T : T +>K : K +>b : () => { [K in keyof U]: U[K]; } +>U : U +>K : K +>U : U +>U : U +>K : K + + a = b; +>a = b : () => { [K in keyof U]: U[K]; } +>a : () => { [K in keyof T]: T[K]; } +>b : () => { [K in keyof U]: U[K]; } + + b = a; +>b = a : () => { [K in keyof T]: T[K]; } +>b : () => { [K in keyof U]: U[K]; } +>a : () => { [K in keyof T]: T[K]; } +} + +// Repro from #18338 + +type IdMapped = { [K in keyof T]: T[K] } +>IdMapped : IdMapped +>T : T +>K : K +>T : T +>T : T +>K : K + +declare const f: () => IdMapped; +>f : () => IdMapped +>T : T +>IdMapped : IdMapped +>T : T + +declare const g: () => { [K in keyof U]: U[K] }; +>g : () => { [K in keyof U]: U[K]; } +>U : U +>K : K +>U : U +>U : U +>K : K + +const h: typeof g = f; +>h : () => { [K in keyof U]: U[K]; } +>g : () => { [K in keyof U]: U[K]; } +>f : () => IdMapped + diff --git a/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts b/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts new file mode 100644 index 0000000000000..9615628391542 --- /dev/null +++ b/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts @@ -0,0 +1,25 @@ +// @strict + +function f1(a: () => keyof T, b: () => keyof U) { + a = b; + b = a; +} + +function f2(a: () => T[K], b: () => U[L]) { + a = b; + b = a; +} + +function f3(a: () => { [K in keyof T]: T[K] }, b: () => { [K in keyof U]: U[K] }) { + a = b; + b = a; +} + +// Repro from #18338 + +type IdMapped = { [K in keyof T]: T[K] } + +declare const f: () => IdMapped; +declare const g: () => { [K in keyof U]: U[K] }; + +const h: typeof g = f;