Skip to content

Commit c770aac

Browse files
committed
Treat primitive key types as empty when relating to indexer
1 parent 5bcf251 commit c770aac

6 files changed

+131
-2
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12633,7 +12633,7 @@ namespace ts {
1263312633
return Ternary.True;
1263412634
}
1263512635
// A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X.
12636-
if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getIndexType(source))) {
12636+
if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), filterType(getIndexType(source), t => !(t.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.ESSymbol))))) {
1263712637
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target));
1263812638
const templateType = getTemplateTypeFromMappedType(target);
1263912639
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {

tests/baselines/reference/indexSignatureAndMappedType.errors.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{
22
tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
33
Type 'U' is not assignable to type 'T'.
44
tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
5+
tests/cases/compiler/indexSignatureAndMappedType.ts(37,5): error TS2322: Type '{ [x: string]: number; }' is not assignable to type 'Record<K, number>'.
6+
tests/cases/compiler/indexSignatureAndMappedType.ts(42,5): error TS2741: Property 'a' is missing in type '{ [x: string]: number; }' but required in type 'Record<"a", number>'.
57

68

7-
==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ====
9+
==== tests/cases/compiler/indexSignatureAndMappedType.ts (5 errors) ====
810
// A mapped type { [P in K]: X }, where K is a generic type, is related to
911
// { [key: string]: Y } if X is related to Y.
1012

@@ -44,4 +46,20 @@ tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{
4446
interface IEntity<T extends string> extends IBaseEntity {
4547
properties: Record<T, string>;
4648
}
49+
50+
// Repro from #28798
51+
52+
function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
53+
x = y; // error
54+
~
55+
!!! error TS2322: Type '{ [x: string]: number; }' is not assignable to type 'Record<K, number>'.
56+
y = x;
57+
}
58+
59+
function concreteRecord(x: Record<'a', number>, y: { [x: string]: number }) {
60+
x = y; // error
61+
~
62+
!!! error TS2741: Property 'a' is missing in type '{ [x: string]: number; }' but required in type 'Record<"a", number>'.
63+
y = x;
64+
}
4765

tests/baselines/reference/indexSignatureAndMappedType.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ interface IBaseEntity {
3131
interface IEntity<T extends string> extends IBaseEntity {
3232
properties: Record<T, string>;
3333
}
34+
35+
// Repro from #28798
36+
37+
function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
38+
x = y; // error
39+
y = x;
40+
}
41+
42+
function concreteRecord(x: Record<'a', number>, y: { [x: string]: number }) {
43+
x = y; // error
44+
y = x;
45+
}
3446

3547

3648
//// [indexSignatureAndMappedType.js]
@@ -49,6 +61,15 @@ function f3(x, y) {
4961
x = y; // Error
5062
y = x; // Error
5163
}
64+
// Repro from #28798
65+
function constrainedRecord(x, y) {
66+
x = y; // error
67+
y = x;
68+
}
69+
function concreteRecord(x, y) {
70+
x = y; // error
71+
y = x;
72+
}
5273

5374

5475
//// [indexSignatureAndMappedType.d.ts]
@@ -71,3 +92,9 @@ interface IBaseEntity {
7192
interface IEntity<T extends string> extends IBaseEntity {
7293
properties: Record<T, string>;
7394
}
95+
declare function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: {
96+
[x: string]: number;
97+
}): void;
98+
declare function concreteRecord(x: Record<'a', number>, y: {
99+
[x: string]: number;
100+
}): void;

tests/baselines/reference/indexSignatureAndMappedType.symbols

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,39 @@ interface IEntity<T extends string> extends IBaseEntity {
9696
>T : Symbol(T, Decl(indexSignatureAndMappedType.ts, 29, 18))
9797
}
9898

99+
// Repro from #28798
100+
101+
function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
102+
>constrainedRecord : Symbol(constrainedRecord, Decl(indexSignatureAndMappedType.ts, 31, 1))
103+
>K : Symbol(K, Decl(indexSignatureAndMappedType.ts, 35, 27))
104+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 35, 42))
105+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
106+
>K : Symbol(K, Decl(indexSignatureAndMappedType.ts, 35, 27))
107+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 35, 63))
108+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 35, 70))
109+
110+
x = y; // error
111+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 35, 42))
112+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 35, 63))
113+
114+
y = x;
115+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 35, 63))
116+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 35, 42))
117+
}
118+
119+
function concreteRecord(x: Record<'a', number>, y: { [x: string]: number }) {
120+
>concreteRecord : Symbol(concreteRecord, Decl(indexSignatureAndMappedType.ts, 38, 1))
121+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 40, 24))
122+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
123+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 40, 47))
124+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 40, 54))
125+
126+
x = y; // error
127+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 40, 24))
128+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 40, 47))
129+
130+
y = x;
131+
>y : Symbol(y, Decl(indexSignatureAndMappedType.ts, 40, 47))
132+
>x : Symbol(x, Decl(indexSignatureAndMappedType.ts, 40, 24))
133+
}
134+

tests/baselines/reference/indexSignatureAndMappedType.types

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,39 @@ interface IEntity<T extends string> extends IBaseEntity {
7676
>properties : Record<T, string>
7777
}
7878

79+
// Repro from #28798
80+
81+
function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
82+
>constrainedRecord : <K extends "a">(x: Record<K, number>, y: { [x: string]: number; }) => void
83+
>x : Record<K, number>
84+
>y : { [x: string]: number; }
85+
>x : string
86+
87+
x = y; // error
88+
>x = y : { [x: string]: number; }
89+
>x : Record<K, number>
90+
>y : { [x: string]: number; }
91+
92+
y = x;
93+
>y = x : Record<K, number>
94+
>y : { [x: string]: number; }
95+
>x : Record<K, number>
96+
}
97+
98+
function concreteRecord(x: Record<'a', number>, y: { [x: string]: number }) {
99+
>concreteRecord : (x: Record<"a", number>, y: { [x: string]: number; }) => void
100+
>x : Record<"a", number>
101+
>y : { [x: string]: number; }
102+
>x : string
103+
104+
x = y; // error
105+
>x = y : { [x: string]: number; }
106+
>x : Record<"a", number>
107+
>y : { [x: string]: number; }
108+
109+
y = x;
110+
>y = x : Record<"a", number>
111+
>y : { [x: string]: number; }
112+
>x : Record<"a", number>
113+
}
114+

tests/cases/compiler/indexSignatureAndMappedType.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@ interface IBaseEntity {
3333
interface IEntity<T extends string> extends IBaseEntity {
3434
properties: Record<T, string>;
3535
}
36+
37+
// Repro from #28798
38+
39+
function constrainedRecord<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
40+
x = y; // error
41+
y = x;
42+
}
43+
44+
function concreteRecord(x: Record<'a', number>, y: { [x: string]: number }) {
45+
x = y; // error
46+
y = x;
47+
}

0 commit comments

Comments
 (0)