Skip to content

Commit dc85623

Browse files
committed
Fix #17069 and #15371
1. `T[K]` now correctly produces `number` when `K extends string, T extends Record<K, number>`. 2. `T[K]` no longer allows any type to be assigned to it when `T extends object, K extends keyof T`. Previously both of these cases failed in getConstraintOfIndexedAccessType because both bases followed `K`'s base constraint to `string` and then incorrectly produced `any` for types (like `object`) with no string index signature. In (1), this produced an error in checkBinaryLikeExpression`. In (2), this failed to produce an error in `checkTypeRelatedTo`.
1 parent 8e5e6c6 commit dc85623

File tree

2 files changed

+13
-8
lines changed

2 files changed

+13
-8
lines changed

src/compiler/checker.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -5911,7 +5911,14 @@ namespace ts {
59115911
return transformed;
59125912
}
59135913
const baseObjectType = getBaseConstraintOfType(type.objectType);
5914-
const baseIndexType = getBaseConstraintOfType(type.indexType);
5914+
const keepTypeParameterForMappedType = baseObjectType && getObjectFlags(baseObjectType) & ObjectFlags.Mapped &&
5915+
type.indexType.flags & TypeFlags.TypeParameter;
5916+
const baseIndexType = !keepTypeParameterForMappedType && getBaseConstraintOfType(type.indexType);
5917+
if (baseObjectType && baseIndexType === stringType && !getIndexInfoOfType(baseObjectType, IndexKind.String)) {
5918+
// getIndexedAccessType returns `any` for X[string] where X doesn't have an index signature.
5919+
// to avoid this, return `undefined`.
5920+
return undefined;
5921+
}
59155922
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
59165923
}
59175924

@@ -5961,8 +5968,9 @@ namespace ts {
59615968
function computeBaseConstraint(t: Type): Type {
59625969
if (t.flags & TypeFlags.TypeParameter) {
59635970
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
5964-
return (<TypeParameter>t).isThisType ? constraint :
5965-
constraint ? getBaseConstraint(constraint) : undefined;
5971+
return (t as TypeParameter).isThisType || !constraint ?
5972+
constraint :
5973+
getBaseConstraint(constraint);
59665974
}
59675975
if (t.flags & TypeFlags.UnionOrIntersection) {
59685976
const types = (<UnionOrIntersectionType>t).types;
@@ -5990,9 +5998,6 @@ namespace ts {
59905998
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
59915999
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
59926000
}
5993-
if (isGenericMappedType(t)) {
5994-
return emptyObjectType;
5995-
}
59966001
return t;
59976002
}
59986003
}
@@ -9289,7 +9294,7 @@ namespace ts {
92899294
}
92909295
else if (target.flags & TypeFlags.IndexedAccess) {
92919296
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
9292-
// A is the apparent type of S.
9297+
// A is the apparent type of T.
92939298
const constraint = getConstraintOfType(<IndexedAccessType>target);
92949299
if (constraint) {
92959300
if (result = isRelatedTo(source, constraint, reportErrors)) {

src/compiler/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ namespace ts {
718718
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
719719
let result = 0;
720720
for (const v of array) {
721-
// Note: we need the following type assertion because of GH #17069
721+
// TODO: Remove the following type assertion once the fix for #17069 is merged
722722
result += v[prop] as number;
723723
}
724724
return result;

0 commit comments

Comments
 (0)