Always substitute indexed generic mapped type when getting constraint from indexed access (#53066)

This commit is contained in:
Mateusz Burzyński
2023-03-21 12:50:11 -07:00
committed by GitHub
parent 377c4ce32f
commit abb4052f2f
6 changed files with 128 additions and 9 deletions
+1 -1
View File
@@ -13573,7 +13573,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
if (isMappedTypeGenericIndexedAccess(type)) {
if (isMappedTypeGenericIndexedAccess(type) || isGenericMappedType(type.objectType)) {
// 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);
@@ -1,10 +1,25 @@
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(10,11): error TS2322: Type 'Mapped2<K>[`get${K}`]' is not assignable to type '{ a: K; }'.
Type 'Mapped2<K>[`get${string}`]' is not assignable to type '{ a: K; }'.
Type '{ a: `get${K}`; }' is not assignable to type '{ a: K; }'.
Types of property 'a' are incompatible.
Type '`get${K}`' is not assignable to type 'K'.
'`get${K}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
Type '`get${string}`' is not assignable to type 'K'.
'`get${string}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(16,11): error TS2322: Type 'Mapped3<K>[Uppercase<K>]' is not assignable to type '{ a: K; }'.
Type 'Mapped3<K>[Uppercase<string>]' is not assignable to type '{ a: K; }'.
Type 'Mapped3<K>[string]' is not assignable to type '{ a: K; }'.
Type '{ a: Uppercase<K>; }' is not assignable to type '{ a: K; }'.
Types of property 'a' are incompatible.
Type 'Uppercase<K>' is not assignable to type 'K'.
'Uppercase<K>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
Type 'Uppercase<string>' is not assignable to type 'K'.
'Uppercase<string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
Type 'string' is not assignable to type 'K'.
'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2322: Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo<T>[`get${T}`]'.
'Foo<T>[`get${T}`]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
Type '`get${T}`' is not assignable to type 'T'.
'`get${T}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
Type '`get${string}`' is not assignable to type 'T'.
'`get${string}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
==== tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts (3 errors) ====
@@ -20,7 +35,12 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
const x: { a: K } = obj[key]; // Error
~
!!! error TS2322: Type 'Mapped2<K>[`get${K}`]' is not assignable to type '{ a: K; }'.
!!! error TS2322: Type 'Mapped2<K>[`get${string}`]' is not assignable to type '{ a: K; }'.
!!! error TS2322: Type '{ a: `get${K}`; }' is not assignable to type '{ a: K; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type '`get${K}`' is not assignable to type 'K'.
!!! error TS2322: '`get${K}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
!!! error TS2322: Type '`get${string}`' is not assignable to type 'K'.
!!! error TS2322: '`get${string}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
}
type Mapped3<K extends string> = { [P in K as Uppercase<P>]: { a: P } };
@@ -29,8 +49,14 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
const x: { a: K } = obj[key]; // Error
~
!!! error TS2322: Type 'Mapped3<K>[Uppercase<K>]' is not assignable to type '{ a: K; }'.
!!! error TS2322: Type 'Mapped3<K>[Uppercase<string>]' is not assignable to type '{ a: K; }'.
!!! error TS2322: Type 'Mapped3<K>[string]' is not assignable to type '{ a: K; }'.
!!! error TS2322: Type '{ a: Uppercase<K>; }' is not assignable to type '{ a: K; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'Uppercase<K>' is not assignable to type 'K'.
!!! error TS2322: 'Uppercase<K>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
!!! error TS2322: Type 'Uppercase<string>' is not assignable to type 'K'.
!!! error TS2322: 'Uppercase<string>' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
!!! error TS2322: Type 'string' is not assignable to type 'K'.
!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'.
}
// Repro from #47794
@@ -42,7 +68,11 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
const get = <T extends string>(t: T, foo: Foo<T>): T => foo[`get${t}`]; // Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'
~~~~~~~~~~~~~~
!!! error TS2322: Type 'Foo<T>[`get${T}`]' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo<T>[`get${T}`]'.
!!! error TS2322: 'Foo<T>[`get${T}`]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
!!! error TS2322: Type '`get${T}`' is not assignable to type 'T'.
!!! error TS2322: '`get${T}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
!!! error TS2322: Type '`get${string}`' is not assignable to type 'T'.
!!! error TS2322: '`get${string}`' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string'.
// Repro from #48626
@@ -65,4 +95,14 @@ tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts(25,57): error TS2
}
return true;
}
// repro from #50030
type ObjectWithUnderscoredKeys<K extends string> = {
[k in K as `_${k}`]: true;
};
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
}
@@ -46,6 +46,16 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
}
return true;
}
// repro from #50030
type ObjectWithUnderscoredKeys<K extends string> = {
[k in K as `_${k}`]: true;
};
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
}
//// [mappedTypeConstraints2.js]
@@ -71,6 +81,9 @@ function validate(obj, bounds) {
}
return true;
}
function genericTest(objectWithUnderscoredKeys, key) {
const shouldBeTrue = objectWithUnderscoredKeys[`_${key}`];
}
//// [mappedTypeConstraints2.d.ts]
@@ -104,3 +117,7 @@ type NumericBoundsOf<T> = {
[K in keyof T as T[K] extends number | undefined ? K : never]: Bounds;
};
declare function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>): boolean;
type ObjectWithUnderscoredKeys<K extends string> = {
[k in K as `_${k}`]: true;
};
declare function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K): void;
@@ -171,3 +171,31 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
return true;
}
// repro from #50030
type ObjectWithUnderscoredKeys<K extends string> = {
>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 46, 1))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 50, 31))
[k in K as `_${k}`]: true;
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 51, 5))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 50, 31))
>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 51, 5))
};
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
>genericTest : Symbol(genericTest, Decl(mappedTypeConstraints2.ts, 52, 2))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 54, 39))
>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 46, 1))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 54, 95))
>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 54, 21))
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
>shouldBeTrue : Symbol(shouldBeTrue, Decl(mappedTypeConstraints2.ts, 55, 7))
>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 54, 39))
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 54, 95))
}
@@ -128,3 +128,27 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
>true : true
}
// repro from #50030
type ObjectWithUnderscoredKeys<K extends string> = {
>ObjectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
[k in K as `_${k}`]: true;
>true : true
};
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
>genericTest : <K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) => void
>objectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
>key : K
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
>shouldBeTrue : true
>true : true
>objectWithUnderscoredKeys[`_${key}`] : ObjectWithUnderscoredKeys<K>[`_${K}`]
>objectWithUnderscoredKeys : ObjectWithUnderscoredKeys<K>
>`_${key}` : `_${K}`
>key : K
}
@@ -49,3 +49,13 @@ function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
}
return true;
}
// repro from #50030
type ObjectWithUnderscoredKeys<K extends string> = {
[k in K as `_${k}`]: true;
};
function genericTest<K extends string>(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys<K>, key: K) {
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`];
}