Skip to content

Commit f050215

Browse files
committed
Avoid deprioritization of inferences made from mapped types with primitive type parameter constraints
1 parent 8749fb5 commit f050215

4 files changed

+183
-1
lines changed

src/compiler/checker.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -25059,9 +25059,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2505925059
applyToReturnTypes(source, target, inferFromTypes);
2506025060
}
2506125061

25062+
function hasPrimitiveMappedTypeParameterConstaint(mappedType: MappedType): boolean {
25063+
const constraint = mappedType.typeParameter && getConstraintOfTypeParameter(mappedType.typeParameter);
25064+
return !!(constraint && constraint.flags & TypeFlags.Primitive);
25065+
}
25066+
2506225067
function inferFromIndexTypes(source: Type, target: Type) {
2506325068
// Inferences across mapped type index signatures are pretty much the same a inferences to homomorphic variables
25064-
const priority = (getObjectFlags(source) & getObjectFlags(target) & ObjectFlags.Mapped) ? InferencePriority.HomomorphicMappedType : 0;
25069+
const priority = (getObjectFlags(source) & getObjectFlags(target) & ObjectFlags.Mapped) &&
25070+
!hasPrimitiveMappedTypeParameterConstaint(source as MappedType) &&
25071+
!hasPrimitiveMappedTypeParameterConstaint(target as MappedType) ?
25072+
InferencePriority.HomomorphicMappedType :
25073+
InferencePriority.None;
2506525074
const indexInfos = getIndexInfosOfType(target);
2506625075
if (isObjectTypeWithInferableIndex(source)) {
2506725076
for (const targetInfo of indexInfos) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
=== tests/cases/compiler/mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts ===
2+
// repro from https://github.com/microsoft/TypeScript/issues/54000
3+
4+
function foo<T>(record: Record<string, T>, entity: T) {}
5+
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
6+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))
7+
>record : Symbol(record, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 16))
8+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
9+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))
10+
>entity : Symbol(entity, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 42))
11+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))
12+
13+
type StringArrayRecord = Record<string, string[]>;
14+
>StringArrayRecord : Symbol(StringArrayRecord, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 56))
15+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
16+
17+
function test() {
18+
>test : Symbol(test, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 4, 50))
19+
20+
const working: Record<string, string[]> = {};
21+
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 7, 7))
22+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
23+
24+
foo(working, []);
25+
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
26+
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 7, 7))
27+
28+
const working2: StringArrayRecord = {};
29+
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 10, 7))
30+
>StringArrayRecord : Symbol(StringArrayRecord, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 56))
31+
32+
foo(working2, []);
33+
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
34+
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 10, 7))
35+
}
36+
37+
// showcase the same behavior with index signature
38+
39+
function bar<T>(record: { [k: string]: T }, entity: T) {}
40+
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
41+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))
42+
>record : Symbol(record, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 16))
43+
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 27))
44+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))
45+
>entity : Symbol(entity, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 43))
46+
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))
47+
48+
type StringArrayIndexSignature = { [k: string]: string[] };
49+
>StringArrayIndexSignature : Symbol(StringArrayIndexSignature, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 57))
50+
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 18, 36))
51+
52+
function test2() {
53+
>test2 : Symbol(test2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 18, 59))
54+
55+
const working: { [k: string]: string[] } = {};
56+
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 7))
57+
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 20))
58+
59+
bar(working, []);
60+
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
61+
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 7))
62+
63+
const working2: StringArrayIndexSignature = {};
64+
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 24, 7))
65+
>StringArrayIndexSignature : Symbol(StringArrayIndexSignature, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 57))
66+
67+
bar(working2, []);
68+
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
69+
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 24, 7))
70+
}
71+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
=== tests/cases/compiler/mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts ===
2+
// repro from https://github.com/microsoft/TypeScript/issues/54000
3+
4+
function foo<T>(record: Record<string, T>, entity: T) {}
5+
>foo : <T>(record: Record<string, T>, entity: T) => void
6+
>record : Record<string, T>
7+
>entity : T
8+
9+
type StringArrayRecord = Record<string, string[]>;
10+
>StringArrayRecord : { [x: string]: string[]; }
11+
12+
function test() {
13+
>test : () => void
14+
15+
const working: Record<string, string[]> = {};
16+
>working : Record<string, string[]>
17+
>{} : {}
18+
19+
foo(working, []);
20+
>foo(working, []) : void
21+
>foo : <T>(record: Record<string, T>, entity: T) => void
22+
>working : Record<string, string[]>
23+
>[] : never[]
24+
25+
const working2: StringArrayRecord = {};
26+
>working2 : StringArrayRecord
27+
>{} : {}
28+
29+
foo(working2, []);
30+
>foo(working2, []) : void
31+
>foo : <T>(record: Record<string, T>, entity: T) => void
32+
>working2 : StringArrayRecord
33+
>[] : never[]
34+
}
35+
36+
// showcase the same behavior with index signature
37+
38+
function bar<T>(record: { [k: string]: T }, entity: T) {}
39+
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
40+
>record : { [k: string]: T; }
41+
>k : string
42+
>entity : T
43+
44+
type StringArrayIndexSignature = { [k: string]: string[] };
45+
>StringArrayIndexSignature : { [k: string]: string[]; }
46+
>k : string
47+
48+
function test2() {
49+
>test2 : () => void
50+
51+
const working: { [k: string]: string[] } = {};
52+
>working : { [k: string]: string[]; }
53+
>k : string
54+
>{} : {}
55+
56+
bar(working, []);
57+
>bar(working, []) : void
58+
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
59+
>working : { [k: string]: string[]; }
60+
>[] : never[]
61+
62+
const working2: StringArrayIndexSignature = {};
63+
>working2 : StringArrayIndexSignature
64+
>{} : {}
65+
66+
bar(working2, []);
67+
>bar(working2, []) : void
68+
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
69+
>working2 : StringArrayIndexSignature
70+
>[] : never[]
71+
}
72+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// repro from https://github.com/microsoft/TypeScript/issues/54000
5+
6+
function foo<T>(record: Record<string, T>, entity: T) {}
7+
8+
type StringArrayRecord = Record<string, string[]>;
9+
10+
function test() {
11+
const working: Record<string, string[]> = {};
12+
foo(working, []);
13+
14+
const working2: StringArrayRecord = {};
15+
foo(working2, []);
16+
}
17+
18+
// showcase the same behavior with index signature
19+
20+
function bar<T>(record: { [k: string]: T }, entity: T) {}
21+
22+
type StringArrayIndexSignature = { [k: string]: string[] };
23+
24+
function test2() {
25+
const working: { [k: string]: string[] } = {};
26+
bar(working, []);
27+
28+
const working2: StringArrayIndexSignature = {};
29+
bar(working2, []);
30+
}

0 commit comments

Comments
 (0)