Skip to content

Commit aa0fc0b

Browse files
authored
Merge pull request #17633 from Microsoft/indexSignatureMappedType
Mapped type and string index signature relations
2 parents 3118e81 + 3efeb1e commit aa0fc0b

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed

src/compiler/checker.ts

+5
Original file line numberDiff line numberDiff line change
@@ -9671,6 +9671,11 @@ namespace ts {
96719671
if (sourceInfo) {
96729672
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
96739673
}
9674+
if (isGenericMappedType(source)) {
9675+
// A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
9676+
// if T is related to U.
9677+
return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
9678+
}
96749679
if (isObjectLiteralType(source)) {
96759680
let related = Ternary.True;
96769681
if (kind === IndexKind.String) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
2+
tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
3+
Type 'U' is not assignable to type 'T'.
4+
tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
5+
6+
7+
==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ====
8+
// A mapped type { [P in K]: X }, where K is a generic type, is related to
9+
// { [key: string]: Y } if X is related to Y.
10+
11+
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
12+
x = y;
13+
y = x; // Error
14+
~
15+
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
16+
}
17+
18+
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
19+
x = y;
20+
y = x;
21+
}
22+
23+
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
24+
x = y; // Error
25+
~
26+
!!! error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
27+
!!! error TS2322: Type 'U' is not assignable to type 'T'.
28+
y = x; // Error
29+
~
30+
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
31+
}
32+
33+
// Repro from #14548
34+
35+
type Dictionary = {
36+
[key: string]: string;
37+
};
38+
39+
interface IBaseEntity {
40+
name: string;
41+
properties: Dictionary;
42+
}
43+
44+
interface IEntity<T extends string> extends IBaseEntity {
45+
properties: Record<T, string>;
46+
}
47+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//// [indexSignatureAndMappedType.ts]
2+
// A mapped type { [P in K]: X }, where K is a generic type, is related to
3+
// { [key: string]: Y } if X is related to Y.
4+
5+
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
6+
x = y;
7+
y = x; // Error
8+
}
9+
10+
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
11+
x = y;
12+
y = x;
13+
}
14+
15+
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
16+
x = y; // Error
17+
y = x; // Error
18+
}
19+
20+
// Repro from #14548
21+
22+
type Dictionary = {
23+
[key: string]: string;
24+
};
25+
26+
interface IBaseEntity {
27+
name: string;
28+
properties: Dictionary;
29+
}
30+
31+
interface IEntity<T extends string> extends IBaseEntity {
32+
properties: Record<T, string>;
33+
}
34+
35+
36+
//// [indexSignatureAndMappedType.js]
37+
"use strict";
38+
// A mapped type { [P in K]: X }, where K is a generic type, is related to
39+
// { [key: string]: Y } if X is related to Y.
40+
function f1(x, y) {
41+
x = y;
42+
y = x; // Error
43+
}
44+
function f2(x, y) {
45+
x = y;
46+
y = x;
47+
}
48+
function f3(x, y) {
49+
x = y; // Error
50+
y = x; // Error
51+
}
52+
53+
54+
//// [indexSignatureAndMappedType.d.ts]
55+
declare function f1<T, K extends string>(x: {
56+
[key: string]: T;
57+
}, y: Record<K, T>): void;
58+
declare function f2<T>(x: {
59+
[key: string]: T;
60+
}, y: Record<string, T>): void;
61+
declare function f3<T, U, K extends string>(x: {
62+
[key: string]: T;
63+
}, y: Record<K, U>): void;
64+
declare type Dictionary = {
65+
[key: string]: string;
66+
};
67+
interface IBaseEntity {
68+
name: string;
69+
properties: Dictionary;
70+
}
71+
interface IEntity<T extends string> extends IBaseEntity {
72+
properties: Record<T, string>;
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// A mapped type { [P in K]: X }, where K is a generic type, is related to
5+
// { [key: string]: Y } if X is related to Y.
6+
7+
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
8+
x = y;
9+
y = x; // Error
10+
}
11+
12+
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
13+
x = y;
14+
y = x;
15+
}
16+
17+
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
18+
x = y; // Error
19+
y = x; // Error
20+
}
21+
22+
// Repro from #14548
23+
24+
type Dictionary = {
25+
[key: string]: string;
26+
};
27+
28+
interface IBaseEntity {
29+
name: string;
30+
properties: Dictionary;
31+
}
32+
33+
interface IEntity<T extends string> extends IBaseEntity {
34+
properties: Record<T, string>;
35+
}

0 commit comments

Comments
 (0)