Skip to content

Commit b8e0ded

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 76fb654 commit b8e0ded

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

src/compiler/checker.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -5912,7 +5912,13 @@ namespace ts {
59125912
return transformed;
59135913
}
59145914
const baseObjectType = getBaseConstraintOfType(type.objectType);
5915-
const baseIndexType = getBaseConstraintOfType(type.indexType);
5915+
const keepTypeParameterForMappedType = baseObjectType && getObjectFlags(baseObjectType) & ObjectFlags.Mapped && 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+
// instead, return undefined so that the indexed access check fails
5920+
return undefined;
5921+
}
59165922
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
59175923
}
59185924

@@ -5962,8 +5968,9 @@ namespace ts {
59625968
function computeBaseConstraint(t: Type): Type {
59635969
if (t.flags & TypeFlags.TypeParameter) {
59645970
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
5965-
return (<TypeParameter>t).isThisType ? constraint :
5966-
constraint ? getBaseConstraint(constraint) : undefined;
5971+
return ((t as TypeParameter).isThisType || !constraint || getObjectFlags(constraint) & ObjectFlags.Mapped) ?
5972+
constraint :
5973+
getBaseConstraint(constraint);
59675974
}
59685975
if (t.flags & TypeFlags.UnionOrIntersection) {
59695976
const types = (<UnionOrIntersectionType>t).types;
@@ -9290,7 +9297,7 @@ namespace ts {
92909297
}
92919298
else if (target.flags & TypeFlags.IndexedAccess) {
92929299
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
9293-
// A is the apparent type of S.
9300+
// A is the apparent type of T.
92949301
const constraint = getConstraintOfType(<IndexedAccessType>target);
92959302
if (constraint) {
92969303
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)