diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e56a0b2caa24..bd6eb100c0967 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11754,6 +11754,11 @@ namespace ts { } function getConstraintFromIndexedAccess(type: IndexedAccessType) { + if (isMappedTypeGenericIndexedAccess(type)) { + // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, + // we substitute an instantiation of E where P is replaced with X. + return substituteIndexedMappedType(type.objectType as MappedType, type.indexType); + } const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType); if (indexConstraint && indexConstraint !== type.indexType) { const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.accessFlags); @@ -11967,6 +11972,11 @@ namespace ts { return constraint ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType; } if (t.flags & TypeFlags.IndexedAccess) { + if (isMappedTypeGenericIndexedAccess(t)) { + // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, + // we substitute an instantiation of E where P is replaced with X. + return getBaseConstraint(substituteIndexedMappedType((t as IndexedAccessType).objectType as MappedType, (t as IndexedAccessType).indexType)); + } const baseObjectType = getBaseConstraint((t as IndexedAccessType).objectType); const baseIndexType = getBaseConstraint((t as IndexedAccessType).indexType); const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (t as IndexedAccessType).accessFlags); @@ -12060,13 +12070,7 @@ namespace ts { * type itself. */ function getApparentType(type: Type): Type { - // We obtain the base constraint for all instantiable types, except indexed access types of the form - // { [P in K]: E }[X], where K is non-generic and X is generic. For those types, we instead substitute an - // instantiation of E where P is replaced with X. We do this because getBaseConstraintOfType directly - // lowers to an instantiation where X's constraint is substituted for X, which isn't always desirable. - const t = !(type.flags & TypeFlags.Instantiable) ? type : - isMappedTypeGenericIndexedAccess(type) ? substituteIndexedMappedType((type as IndexedAccessType).objectType as MappedType, (type as IndexedAccessType).indexType) : - getBaseConstraintOfType(type) || unknownType; + const t = !(type.flags & TypeFlags.Instantiable) ? type : getBaseConstraintOfType(type) || unknownType; return getObjectFlags(t) & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t as MappedType) : t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t as IntersectionType) : t.flags & TypeFlags.StringLike ? globalStringType : @@ -19225,6 +19229,17 @@ namespace ts { resetErrorInfo(saveErrorInfo); return result; } + if (isMappedTypeGenericIndexedAccess(source)) { + // For an indexed access type { [P in K]: E}[X], above we have already explored an instantiation of E with X + // substituted for P. We also want to explore type { [P in K]: E }[C], where C is the constraint of X. + const indexConstraint = getConstraintOfType((source as IndexedAccessType).indexType); + if (indexConstraint) { + if (result = isRelatedTo(getIndexedAccessType((source as IndexedAccessType).objectType, indexConstraint), target, RecursionFlags.Source, reportErrors)) { + resetErrorInfo(saveErrorInfo); + return result; + } + } + } } } else if (source.flags & TypeFlags.Index) { diff --git a/tests/baselines/reference/correlatedUnions.js b/tests/baselines/reference/correlatedUnions.js index 7d0d71e9beea2..b42a48c68a0e2 100644 --- a/tests/baselines/reference/correlatedUnions.js +++ b/tests/baselines/reference/correlatedUnions.js @@ -157,6 +157,30 @@ function ff1() { const x1 = apply('sum', 1, 2) const x2 = apply('concat', 'str1', 'str2', 'str3' ) } + +// Repro from #47368 + +type ArgMap = { a: number, b: string }; +type Func = (x: ArgMap[K]) => void; +type Funcs = { [K in keyof ArgMap]: Func }; + +function f1(funcs: Funcs, key: K, arg: ArgMap[K]) { + funcs[key](arg); +} + +function f2(funcs: Funcs, key: K, arg: ArgMap[K]) { + const func = funcs[key]; // Type Funcs[K] + func(arg); +} + +function f3(funcs: Funcs, key: K, arg: ArgMap[K]) { + const func: Func = funcs[key]; // Error, Funcs[K] not assignable to Func + func(arg); +} + +function f4(x: Funcs[keyof ArgMap], y: Funcs[K]) { + x = y; +} //// [correlatedUnions.js] @@ -244,6 +268,20 @@ function ff1() { var x1 = apply('sum', 1, 2); var x2 = apply('concat', 'str1', 'str2', 'str3'); } +function f1(funcs, key, arg) { + funcs[key](arg); +} +function f2(funcs, key, arg) { + var func = funcs[key]; // Type Funcs[K] + func(arg); +} +function f3(funcs, key, arg) { + var func = funcs[key]; // Error, Funcs[K] not assignable to Func + func(arg); +} +function f4(x, y) { + x = y; +} //// [correlatedUnions.d.ts] @@ -348,3 +386,15 @@ declare const scrollEvent: { readonly callback: (ev: Event) => void; }; declare function ff1(): void; +declare type ArgMap = { + a: number; + b: string; +}; +declare type Func = (x: ArgMap[K]) => void; +declare type Funcs = { + [K in keyof ArgMap]: Func; +}; +declare function f1(funcs: Funcs, key: K, arg: ArgMap[K]): void; +declare function f2(funcs: Funcs, key: K, arg: ArgMap[K]): void; +declare function f3(funcs: Funcs, key: K, arg: ArgMap[K]): void; +declare function f4(x: Funcs[keyof ArgMap], y: Funcs[K]): void; diff --git a/tests/baselines/reference/correlatedUnions.symbols b/tests/baselines/reference/correlatedUnions.symbols index ecb57667c815c..040c3983e95dd 100644 --- a/tests/baselines/reference/correlatedUnions.symbols +++ b/tests/baselines/reference/correlatedUnions.symbols @@ -573,3 +573,105 @@ function ff1() { >apply : Symbol(apply, Decl(correlatedUnions.ts, 150, 5)) } +// Repro from #47368 + +type ArgMap = { a: number, b: string }; +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>a : Symbol(a, Decl(correlatedUnions.ts, 161, 15)) +>b : Symbol(b, Decl(correlatedUnions.ts, 161, 26)) + +type Func = (x: ArgMap[K]) => void; +>Func : Symbol(Func, Decl(correlatedUnions.ts, 161, 39)) +>K : Symbol(K, Decl(correlatedUnions.ts, 162, 10)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>x : Symbol(x, Decl(correlatedUnions.ts, 162, 37)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 162, 10)) + +type Funcs = { [K in keyof ArgMap]: Func }; +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>K : Symbol(K, Decl(correlatedUnions.ts, 163, 16)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>Func : Symbol(Func, Decl(correlatedUnions.ts, 161, 39)) +>K : Symbol(K, Decl(correlatedUnions.ts, 163, 16)) + +function f1(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f1 : Symbol(f1, Decl(correlatedUnions.ts, 163, 46)) +>K : Symbol(K, Decl(correlatedUnions.ts, 165, 12)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 165, 36)) +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>key : Symbol(key, Decl(correlatedUnions.ts, 165, 49)) +>K : Symbol(K, Decl(correlatedUnions.ts, 165, 12)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 165, 57)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 165, 12)) + + funcs[key](arg); +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 165, 36)) +>key : Symbol(key, Decl(correlatedUnions.ts, 165, 49)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 165, 57)) +} + +function f2(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f2 : Symbol(f2, Decl(correlatedUnions.ts, 167, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 169, 12)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 169, 36)) +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>key : Symbol(key, Decl(correlatedUnions.ts, 169, 49)) +>K : Symbol(K, Decl(correlatedUnions.ts, 169, 12)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 169, 57)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 169, 12)) + + const func = funcs[key]; // Type Funcs[K] +>func : Symbol(func, Decl(correlatedUnions.ts, 170, 9)) +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 169, 36)) +>key : Symbol(key, Decl(correlatedUnions.ts, 169, 49)) + + func(arg); +>func : Symbol(func, Decl(correlatedUnions.ts, 170, 9)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 169, 57)) +} + +function f3(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f3 : Symbol(f3, Decl(correlatedUnions.ts, 172, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 174, 12)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 174, 36)) +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>key : Symbol(key, Decl(correlatedUnions.ts, 174, 49)) +>K : Symbol(K, Decl(correlatedUnions.ts, 174, 12)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 174, 57)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 174, 12)) + + const func: Func = funcs[key]; // Error, Funcs[K] not assignable to Func +>func : Symbol(func, Decl(correlatedUnions.ts, 175, 9)) +>Func : Symbol(Func, Decl(correlatedUnions.ts, 161, 39)) +>K : Symbol(K, Decl(correlatedUnions.ts, 174, 12)) +>funcs : Symbol(funcs, Decl(correlatedUnions.ts, 174, 36)) +>key : Symbol(key, Decl(correlatedUnions.ts, 174, 49)) + + func(arg); +>func : Symbol(func, Decl(correlatedUnions.ts, 175, 9)) +>arg : Symbol(arg, Decl(correlatedUnions.ts, 174, 57)) +} + +function f4(x: Funcs[keyof ArgMap], y: Funcs[K]) { +>f4 : Symbol(f4, Decl(correlatedUnions.ts, 177, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 179, 12)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>x : Symbol(x, Decl(correlatedUnions.ts, 179, 36)) +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 157, 1)) +>y : Symbol(y, Decl(correlatedUnions.ts, 179, 59)) +>Funcs : Symbol(Funcs, Decl(correlatedUnions.ts, 162, 59)) +>K : Symbol(K, Decl(correlatedUnions.ts, 179, 12)) + + x = y; +>x : Symbol(x, Decl(correlatedUnions.ts, 179, 36)) +>y : Symbol(y, Decl(correlatedUnions.ts, 179, 59)) +} + diff --git a/tests/baselines/reference/correlatedUnions.types b/tests/baselines/reference/correlatedUnions.types index e10785790a2eb..ae14133757619 100644 --- a/tests/baselines/reference/correlatedUnions.types +++ b/tests/baselines/reference/correlatedUnions.types @@ -549,3 +549,78 @@ function ff1() { >'str3' : "str3" } +// Repro from #47368 + +type ArgMap = { a: number, b: string }; +>ArgMap : ArgMap +>a : number +>b : string + +type Func = (x: ArgMap[K]) => void; +>Func : Func +>x : ArgMap[K] + +type Funcs = { [K in keyof ArgMap]: Func }; +>Funcs : Funcs + +function f1(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f1 : (funcs: Funcs, key: K, arg: ArgMap[K]) => void +>funcs : Funcs +>key : K +>arg : ArgMap[K] + + funcs[key](arg); +>funcs[key](arg) : void +>funcs[key] : Funcs[K] +>funcs : Funcs +>key : K +>arg : ArgMap[K] +} + +function f2(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f2 : (funcs: Funcs, key: K, arg: ArgMap[K]) => void +>funcs : Funcs +>key : K +>arg : ArgMap[K] + + const func = funcs[key]; // Type Funcs[K] +>func : Funcs[K] +>funcs[key] : Funcs[K] +>funcs : Funcs +>key : K + + func(arg); +>func(arg) : void +>func : Funcs[K] +>arg : ArgMap[K] +} + +function f3(funcs: Funcs, key: K, arg: ArgMap[K]) { +>f3 : (funcs: Funcs, key: K, arg: ArgMap[K]) => void +>funcs : Funcs +>key : K +>arg : ArgMap[K] + + const func: Func = funcs[key]; // Error, Funcs[K] not assignable to Func +>func : Func +>funcs[key] : Funcs[K] +>funcs : Funcs +>key : K + + func(arg); +>func(arg) : void +>func : Func +>arg : ArgMap[K] +} + +function f4(x: Funcs[keyof ArgMap], y: Funcs[K]) { +>f4 : (x: Funcs[keyof ArgMap], y: Funcs[K]) => void +>x : Func<"b"> | Func<"a"> +>y : Funcs[K] + + x = y; +>x = y : Funcs[K] +>x : Func<"b"> | Func<"a"> +>y : Funcs[K] +} + diff --git a/tests/cases/compiler/correlatedUnions.ts b/tests/cases/compiler/correlatedUnions.ts index b0b3aa63faa71..546d511034bae 100644 --- a/tests/cases/compiler/correlatedUnions.ts +++ b/tests/cases/compiler/correlatedUnions.ts @@ -159,3 +159,27 @@ function ff1() { const x1 = apply('sum', 1, 2) const x2 = apply('concat', 'str1', 'str2', 'str3' ) } + +// Repro from #47368 + +type ArgMap = { a: number, b: string }; +type Func = (x: ArgMap[K]) => void; +type Funcs = { [K in keyof ArgMap]: Func }; + +function f1(funcs: Funcs, key: K, arg: ArgMap[K]) { + funcs[key](arg); +} + +function f2(funcs: Funcs, key: K, arg: ArgMap[K]) { + const func = funcs[key]; // Type Funcs[K] + func(arg); +} + +function f3(funcs: Funcs, key: K, arg: ArgMap[K]) { + const func: Func = funcs[key]; // Error, Funcs[K] not assignable to Func + func(arg); +} + +function f4(x: Funcs[keyof ArgMap], y: Funcs[K]) { + x = y; +}