Skip to content

Commit 1bac449

Browse files
ahejlsbergKingwl
authored andcommitted
Check combined intersection properties against target index signatures (microsoft#35143)
* Check combined intersection properties against target index signatures * Add tests * Accept new baselines * Less aggressive check for index signatures * Track intersection membership state for both source and target * Minor fixes
1 parent ace9a15 commit 1bac449

6 files changed

+467
-78
lines changed

src/compiler/checker.ts

Lines changed: 78 additions & 78 deletions
Large diffs are not rendered by default.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts(17,1): error TS2322: Type '{ x: A; } & { y: B; }' is not assignable to type '{ [key: string]: A; }'.
2+
Property 'y' is incompatible with index signature.
3+
Property 'a' is missing in type 'B' but required in type 'A'.
4+
tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts(27,10): error TS2339: Property 'b' does not exist on type '{ a: string; }'.
5+
tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts(29,7): error TS2322: Type 'constr<{}, { [key: string]: { a: string; }; }>' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
6+
Index signatures are incompatible.
7+
Property 'b' is missing in type '{ a: string; }' but required in type '{ a: string; b: string; }'.
8+
tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts(35,1): error TS2322: Type '{ a: string; } & { b: number; }' is not assignable to type '{ [key: string]: string; }'.
9+
Property 'b' is incompatible with index signature.
10+
Type 'number' is not assignable to type 'string'.
11+
12+
13+
==== tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts (4 errors) ====
14+
type A = { a: string };
15+
type B = { b: string };
16+
17+
declare let sa1: { x: A & B };
18+
declare let sa2: { x: A } & { x: B };
19+
declare let ta1: { [key: string]: A & B };
20+
declare let ta2: { [key: string]: A } & { [key: string]: B };
21+
22+
ta1 = sa1;
23+
ta1 = sa2;
24+
ta2 = sa1;
25+
ta2 = sa2;
26+
27+
declare let sb1: { x: A } & { y: B };
28+
declare let tb1: { [key: string]: A };
29+
30+
tb1 = sb1; // Error
31+
~~~
32+
!!! error TS2322: Type '{ x: A; } & { y: B; }' is not assignable to type '{ [key: string]: A; }'.
33+
!!! error TS2322: Property 'y' is incompatible with index signature.
34+
!!! error TS2322: Property 'a' is missing in type 'B' but required in type 'A'.
35+
!!! related TS2728 tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts:1:12: 'a' is declared here.
36+
37+
// Repro from #32484
38+
39+
type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<keyof Tgt, keyof Source>>;
40+
41+
type s = constr<{}, { [key: string]: { a: string } }>;
42+
43+
declare const q: s;
44+
q["asd"].a.substr(1);
45+
q["asd"].b; // Error
46+
~
47+
!!! error TS2339: Property 'b' does not exist on type '{ a: string; }'.
48+
49+
const d: { [key: string]: {a: string, b: string} } = q; // Error
50+
~
51+
!!! error TS2322: Type 'constr<{}, { [key: string]: { a: string; }; }>' is not assignable to type '{ [key: string]: { a: string; b: string; }; }'.
52+
!!! error TS2322: Index signatures are incompatible.
53+
!!! error TS2322: Property 'b' is missing in type '{ a: string; }' but required in type '{ a: string; b: string; }'.
54+
!!! related TS2728 tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts:29:39: 'b' is declared here.
55+
56+
// Repro from #32484
57+
58+
declare let ss: { a: string } & { b: number };
59+
declare let tt: { [key: string]: string };
60+
tt = ss; // Error
61+
~~
62+
!!! error TS2322: Type '{ a: string; } & { b: number; }' is not assignable to type '{ [key: string]: string; }'.
63+
!!! error TS2322: Property 'b' is incompatible with index signature.
64+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
65+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [intersectionWithIndexSignatures.ts]
2+
type A = { a: string };
3+
type B = { b: string };
4+
5+
declare let sa1: { x: A & B };
6+
declare let sa2: { x: A } & { x: B };
7+
declare let ta1: { [key: string]: A & B };
8+
declare let ta2: { [key: string]: A } & { [key: string]: B };
9+
10+
ta1 = sa1;
11+
ta1 = sa2;
12+
ta2 = sa1;
13+
ta2 = sa2;
14+
15+
declare let sb1: { x: A } & { y: B };
16+
declare let tb1: { [key: string]: A };
17+
18+
tb1 = sb1; // Error
19+
20+
// Repro from #32484
21+
22+
type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<keyof Tgt, keyof Source>>;
23+
24+
type s = constr<{}, { [key: string]: { a: string } }>;
25+
26+
declare const q: s;
27+
q["asd"].a.substr(1);
28+
q["asd"].b; // Error
29+
30+
const d: { [key: string]: {a: string, b: string} } = q; // Error
31+
32+
// Repro from #32484
33+
34+
declare let ss: { a: string } & { b: number };
35+
declare let tt: { [key: string]: string };
36+
tt = ss; // Error
37+
38+
39+
//// [intersectionWithIndexSignatures.js]
40+
"use strict";
41+
ta1 = sa1;
42+
ta1 = sa2;
43+
ta2 = sa1;
44+
ta2 = sa2;
45+
tb1 = sb1; // Error
46+
q["asd"].a.substr(1);
47+
q["asd"].b; // Error
48+
var d = q; // Error
49+
tt = ss; // Error
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
=== tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts ===
2+
type A = { a: string };
3+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
4+
>a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 0, 10))
5+
6+
type B = { b: string };
7+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
8+
>b : Symbol(b, Decl(intersectionWithIndexSignatures.ts, 1, 10))
9+
10+
declare let sa1: { x: A & B };
11+
>sa1 : Symbol(sa1, Decl(intersectionWithIndexSignatures.ts, 3, 11))
12+
>x : Symbol(x, Decl(intersectionWithIndexSignatures.ts, 3, 18))
13+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
14+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
15+
16+
declare let sa2: { x: A } & { x: B };
17+
>sa2 : Symbol(sa2, Decl(intersectionWithIndexSignatures.ts, 4, 11))
18+
>x : Symbol(x, Decl(intersectionWithIndexSignatures.ts, 4, 18))
19+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
20+
>x : Symbol(x, Decl(intersectionWithIndexSignatures.ts, 4, 29))
21+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
22+
23+
declare let ta1: { [key: string]: A & B };
24+
>ta1 : Symbol(ta1, Decl(intersectionWithIndexSignatures.ts, 5, 11))
25+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 5, 20))
26+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
27+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
28+
29+
declare let ta2: { [key: string]: A } & { [key: string]: B };
30+
>ta2 : Symbol(ta2, Decl(intersectionWithIndexSignatures.ts, 6, 11))
31+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 6, 20))
32+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
33+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 6, 43))
34+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
35+
36+
ta1 = sa1;
37+
>ta1 : Symbol(ta1, Decl(intersectionWithIndexSignatures.ts, 5, 11))
38+
>sa1 : Symbol(sa1, Decl(intersectionWithIndexSignatures.ts, 3, 11))
39+
40+
ta1 = sa2;
41+
>ta1 : Symbol(ta1, Decl(intersectionWithIndexSignatures.ts, 5, 11))
42+
>sa2 : Symbol(sa2, Decl(intersectionWithIndexSignatures.ts, 4, 11))
43+
44+
ta2 = sa1;
45+
>ta2 : Symbol(ta2, Decl(intersectionWithIndexSignatures.ts, 6, 11))
46+
>sa1 : Symbol(sa1, Decl(intersectionWithIndexSignatures.ts, 3, 11))
47+
48+
ta2 = sa2;
49+
>ta2 : Symbol(ta2, Decl(intersectionWithIndexSignatures.ts, 6, 11))
50+
>sa2 : Symbol(sa2, Decl(intersectionWithIndexSignatures.ts, 4, 11))
51+
52+
declare let sb1: { x: A } & { y: B };
53+
>sb1 : Symbol(sb1, Decl(intersectionWithIndexSignatures.ts, 13, 11))
54+
>x : Symbol(x, Decl(intersectionWithIndexSignatures.ts, 13, 18))
55+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
56+
>y : Symbol(y, Decl(intersectionWithIndexSignatures.ts, 13, 29))
57+
>B : Symbol(B, Decl(intersectionWithIndexSignatures.ts, 0, 23))
58+
59+
declare let tb1: { [key: string]: A };
60+
>tb1 : Symbol(tb1, Decl(intersectionWithIndexSignatures.ts, 14, 11))
61+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 14, 20))
62+
>A : Symbol(A, Decl(intersectionWithIndexSignatures.ts, 0, 0))
63+
64+
tb1 = sb1; // Error
65+
>tb1 : Symbol(tb1, Decl(intersectionWithIndexSignatures.ts, 14, 11))
66+
>sb1 : Symbol(sb1, Decl(intersectionWithIndexSignatures.ts, 13, 11))
67+
68+
// Repro from #32484
69+
70+
type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<keyof Tgt, keyof Source>>;
71+
>constr : Symbol(constr, Decl(intersectionWithIndexSignatures.ts, 16, 10))
72+
>Source : Symbol(Source, Decl(intersectionWithIndexSignatures.ts, 20, 12))
73+
>Tgt : Symbol(Tgt, Decl(intersectionWithIndexSignatures.ts, 20, 19))
74+
>K : Symbol(K, Decl(intersectionWithIndexSignatures.ts, 20, 30))
75+
>Source : Symbol(Source, Decl(intersectionWithIndexSignatures.ts, 20, 12))
76+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
77+
>Tgt : Symbol(Tgt, Decl(intersectionWithIndexSignatures.ts, 20, 19))
78+
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
79+
>Tgt : Symbol(Tgt, Decl(intersectionWithIndexSignatures.ts, 20, 19))
80+
>Source : Symbol(Source, Decl(intersectionWithIndexSignatures.ts, 20, 12))
81+
82+
type s = constr<{}, { [key: string]: { a: string } }>;
83+
>s : Symbol(s, Decl(intersectionWithIndexSignatures.ts, 20, 105))
84+
>constr : Symbol(constr, Decl(intersectionWithIndexSignatures.ts, 16, 10))
85+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 22, 23))
86+
>a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 22, 38))
87+
88+
declare const q: s;
89+
>q : Symbol(q, Decl(intersectionWithIndexSignatures.ts, 24, 13))
90+
>s : Symbol(s, Decl(intersectionWithIndexSignatures.ts, 20, 105))
91+
92+
q["asd"].a.substr(1);
93+
>q["asd"].a.substr : Symbol(String.substr, Decl(lib.es5.d.ts, --, --))
94+
>q["asd"].a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 22, 38))
95+
>q : Symbol(q, Decl(intersectionWithIndexSignatures.ts, 24, 13))
96+
>a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 22, 38))
97+
>substr : Symbol(String.substr, Decl(lib.es5.d.ts, --, --))
98+
99+
q["asd"].b; // Error
100+
>q : Symbol(q, Decl(intersectionWithIndexSignatures.ts, 24, 13))
101+
102+
const d: { [key: string]: {a: string, b: string} } = q; // Error
103+
>d : Symbol(d, Decl(intersectionWithIndexSignatures.ts, 28, 5))
104+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 28, 12))
105+
>a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 28, 27))
106+
>b : Symbol(b, Decl(intersectionWithIndexSignatures.ts, 28, 37))
107+
>q : Symbol(q, Decl(intersectionWithIndexSignatures.ts, 24, 13))
108+
109+
// Repro from #32484
110+
111+
declare let ss: { a: string } & { b: number };
112+
>ss : Symbol(ss, Decl(intersectionWithIndexSignatures.ts, 32, 11))
113+
>a : Symbol(a, Decl(intersectionWithIndexSignatures.ts, 32, 17))
114+
>b : Symbol(b, Decl(intersectionWithIndexSignatures.ts, 32, 33))
115+
116+
declare let tt: { [key: string]: string };
117+
>tt : Symbol(tt, Decl(intersectionWithIndexSignatures.ts, 33, 11))
118+
>key : Symbol(key, Decl(intersectionWithIndexSignatures.ts, 33, 19))
119+
120+
tt = ss; // Error
121+
>tt : Symbol(tt, Decl(intersectionWithIndexSignatures.ts, 33, 11))
122+
>ss : Symbol(ss, Decl(intersectionWithIndexSignatures.ts, 32, 11))
123+
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
=== tests/cases/conformance/types/intersection/intersectionWithIndexSignatures.ts ===
2+
type A = { a: string };
3+
>A : A
4+
>a : string
5+
6+
type B = { b: string };
7+
>B : B
8+
>b : string
9+
10+
declare let sa1: { x: A & B };
11+
>sa1 : { x: A & B; }
12+
>x : A & B
13+
14+
declare let sa2: { x: A } & { x: B };
15+
>sa2 : { x: A; } & { x: B; }
16+
>x : A
17+
>x : B
18+
19+
declare let ta1: { [key: string]: A & B };
20+
>ta1 : { [key: string]: A & B; }
21+
>key : string
22+
23+
declare let ta2: { [key: string]: A } & { [key: string]: B };
24+
>ta2 : { [key: string]: A; } & { [key: string]: B; }
25+
>key : string
26+
>key : string
27+
28+
ta1 = sa1;
29+
>ta1 = sa1 : { x: A & B; }
30+
>ta1 : { [key: string]: A & B; }
31+
>sa1 : { x: A & B; }
32+
33+
ta1 = sa2;
34+
>ta1 = sa2 : { x: A; } & { x: B; }
35+
>ta1 : { [key: string]: A & B; }
36+
>sa2 : { x: A; } & { x: B; }
37+
38+
ta2 = sa1;
39+
>ta2 = sa1 : { x: A & B; }
40+
>ta2 : { [key: string]: A; } & { [key: string]: B; }
41+
>sa1 : { x: A & B; }
42+
43+
ta2 = sa2;
44+
>ta2 = sa2 : { x: A; } & { x: B; }
45+
>ta2 : { [key: string]: A; } & { [key: string]: B; }
46+
>sa2 : { x: A; } & { x: B; }
47+
48+
declare let sb1: { x: A } & { y: B };
49+
>sb1 : { x: A; } & { y: B; }
50+
>x : A
51+
>y : B
52+
53+
declare let tb1: { [key: string]: A };
54+
>tb1 : { [key: string]: A; }
55+
>key : string
56+
57+
tb1 = sb1; // Error
58+
>tb1 = sb1 : { x: A; } & { y: B; }
59+
>tb1 : { [key: string]: A; }
60+
>sb1 : { x: A; } & { y: B; }
61+
62+
// Repro from #32484
63+
64+
type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<keyof Tgt, keyof Source>>;
65+
>constr : constr<Source, Tgt>
66+
67+
type s = constr<{}, { [key: string]: { a: string } }>;
68+
>s : constr<{}, { [key: string]: { a: string; }; }>
69+
>key : string
70+
>a : string
71+
72+
declare const q: s;
73+
>q : constr<{}, { [key: string]: { a: string; }; }>
74+
75+
q["asd"].a.substr(1);
76+
>q["asd"].a.substr(1) : string
77+
>q["asd"].a.substr : (from: number, length?: number | undefined) => string
78+
>q["asd"].a : string
79+
>q["asd"] : { a: string; }
80+
>q : constr<{}, { [key: string]: { a: string; }; }>
81+
>"asd" : "asd"
82+
>a : string
83+
>substr : (from: number, length?: number | undefined) => string
84+
>1 : 1
85+
86+
q["asd"].b; // Error
87+
>q["asd"].b : any
88+
>q["asd"] : { a: string; }
89+
>q : constr<{}, { [key: string]: { a: string; }; }>
90+
>"asd" : "asd"
91+
>b : any
92+
93+
const d: { [key: string]: {a: string, b: string} } = q; // Error
94+
>d : { [key: string]: { a: string; b: string; }; }
95+
>key : string
96+
>a : string
97+
>b : string
98+
>q : constr<{}, { [key: string]: { a: string; }; }>
99+
100+
// Repro from #32484
101+
102+
declare let ss: { a: string } & { b: number };
103+
>ss : { a: string; } & { b: number; }
104+
>a : string
105+
>b : number
106+
107+
declare let tt: { [key: string]: string };
108+
>tt : { [key: string]: string; }
109+
>key : string
110+
111+
tt = ss; // Error
112+
>tt = ss : { a: string; } & { b: number; }
113+
>tt : { [key: string]: string; }
114+
>ss : { a: string; } & { b: number; }
115+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// @strict: true
2+
3+
type A = { a: string };
4+
type B = { b: string };
5+
6+
declare let sa1: { x: A & B };
7+
declare let sa2: { x: A } & { x: B };
8+
declare let ta1: { [key: string]: A & B };
9+
declare let ta2: { [key: string]: A } & { [key: string]: B };
10+
11+
ta1 = sa1;
12+
ta1 = sa2;
13+
ta2 = sa1;
14+
ta2 = sa2;
15+
16+
declare let sb1: { x: A } & { y: B };
17+
declare let tb1: { [key: string]: A };
18+
19+
tb1 = sb1; // Error
20+
21+
// Repro from #32484
22+
23+
type constr<Source, Tgt> = { [K in keyof Source]: string } & Pick<Tgt, Exclude<keyof Tgt, keyof Source>>;
24+
25+
type s = constr<{}, { [key: string]: { a: string } }>;
26+
27+
declare const q: s;
28+
q["asd"].a.substr(1);
29+
q["asd"].b; // Error
30+
31+
const d: { [key: string]: {a: string, b: string} } = q; // Error
32+
33+
// Repro from #32484
34+
35+
declare let ss: { a: string } & { b: number };
36+
declare let tt: { [key: string]: string };
37+
tt = ss; // Error

0 commit comments

Comments
 (0)