Skip to content

Commit 7a3e68f

Browse files
authored
Only return the substitute in substitution instantiation when assignability fails (rather than subtype) (#31027)
* Only return the substitute in substitution instantiation when assignability fails (rather than subtype) * Add test from issue
1 parent 40a2eb2 commit 7a3e68f

File tree

5 files changed

+190
-1
lines changed

5 files changed

+190
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11295,7 +11295,7 @@ namespace ts {
1129511295
}
1129611296
else {
1129711297
const sub = instantiateType((<SubstitutionType>type).substitute, mapper);
11298-
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeSubtypeOf(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
11298+
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
1129911299
return maybeVariable;
1130011300
}
1130111301
return sub;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [substitutionTypeNoMergeOfAssignableType.ts]
2+
interface Entry {
3+
comment?: string;
4+
}
5+
6+
interface Entity {
7+
fields: {[key: string]: Entry};
8+
}
9+
10+
type Fields<E extends Entity> = {
11+
[P in keyof E["fields"]]: E["fields"][P]
12+
};
13+
14+
type Nodes<T = any> = {
15+
[P in keyof T]: T[P] extends Entity
16+
? Fields<T[P]>
17+
: T[P]
18+
};
19+
20+
function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
21+
return {} as Nodes<T>
22+
}
23+
24+
const myTest = makeEntityStore({ test: { fields: { id: {} } } });
25+
myTest.test
26+
27+
28+
//// [substitutionTypeNoMergeOfAssignableType.js]
29+
function makeEntityStore(config) {
30+
return {};
31+
}
32+
var myTest = makeEntityStore({ test: { fields: { id: {} } } });
33+
myTest.test;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
=== tests/cases/compiler/substitutionTypeNoMergeOfAssignableType.ts ===
2+
interface Entry {
3+
>Entry : Symbol(Entry, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 0))
4+
5+
comment?: string;
6+
>comment : Symbol(Entry.comment, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 17))
7+
}
8+
9+
interface Entity {
10+
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))
11+
12+
fields: {[key: string]: Entry};
13+
>fields : Symbol(Entity.fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 4, 19))
14+
>key : Symbol(key, Decl(substitutionTypeNoMergeOfAssignableType.ts, 5, 15))
15+
>Entry : Symbol(Entry, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 0))
16+
}
17+
18+
type Fields<E extends Entity> = {
19+
>Fields : Symbol(Fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 6, 2))
20+
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
21+
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))
22+
23+
[P in keyof E["fields"]]: E["fields"][P]
24+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 9, 6))
25+
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
26+
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
27+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 9, 6))
28+
29+
};
30+
31+
type Nodes<T = any> = {
32+
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
33+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
34+
35+
[P in keyof T]: T[P] extends Entity
36+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
37+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
38+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
39+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
40+
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))
41+
42+
? Fields<T[P]>
43+
>Fields : Symbol(Fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 6, 2))
44+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
45+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
46+
47+
: T[P]
48+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
49+
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
50+
51+
};
52+
53+
function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
54+
>makeEntityStore : Symbol(makeEntityStore, Decl(substitutionTypeNoMergeOfAssignableType.ts, 16, 3))
55+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
56+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
57+
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))
58+
>config : Symbol(config, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 60))
59+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
60+
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
61+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
62+
63+
return {} as Nodes<T>
64+
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
65+
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
66+
}
67+
68+
const myTest = makeEntityStore({ test: { fields: { id: {} } } });
69+
>myTest : Symbol(myTest, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 6))
70+
>makeEntityStore : Symbol(makeEntityStore, Decl(substitutionTypeNoMergeOfAssignableType.ts, 16, 3))
71+
>test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))
72+
>fields : Symbol(fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 41))
73+
>id : Symbol(id, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 51))
74+
75+
myTest.test
76+
>myTest.test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))
77+
>myTest : Symbol(myTest, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 6))
78+
>test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))
79+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/substitutionTypeNoMergeOfAssignableType.ts ===
2+
interface Entry {
3+
comment?: string;
4+
>comment : string
5+
}
6+
7+
interface Entity {
8+
fields: {[key: string]: Entry};
9+
>fields : { [key: string]: Entry; }
10+
>key : string
11+
}
12+
13+
type Fields<E extends Entity> = {
14+
>Fields : Fields<E>
15+
16+
[P in keyof E["fields"]]: E["fields"][P]
17+
};
18+
19+
type Nodes<T = any> = {
20+
>Nodes : Nodes<T>
21+
22+
[P in keyof T]: T[P] extends Entity
23+
? Fields<T[P]>
24+
: T[P]
25+
};
26+
27+
function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
28+
>makeEntityStore : <T extends Record<string, Entity>>(config: T) => Nodes<T>
29+
>config : T
30+
31+
return {} as Nodes<T>
32+
>{} as Nodes<T> : Nodes<T>
33+
>{} : {}
34+
}
35+
36+
const myTest = makeEntityStore({ test: { fields: { id: {} } } });
37+
>myTest : Nodes<{ test: { fields: { id: {}; }; }; }>
38+
>makeEntityStore({ test: { fields: { id: {} } } }) : Nodes<{ test: { fields: { id: {}; }; }; }>
39+
>makeEntityStore : <T extends Record<string, Entity>>(config: T) => Nodes<T>
40+
>{ test: { fields: { id: {} } } } : { test: { fields: { id: {}; }; }; }
41+
>test : { fields: { id: {}; }; }
42+
>{ fields: { id: {} } } : { fields: { id: {}; }; }
43+
>fields : { id: {}; }
44+
>{ id: {} } : { id: {}; }
45+
>id : {}
46+
>{} : {}
47+
48+
myTest.test
49+
>myTest.test : Fields<{ fields: { id: {}; }; }>
50+
>myTest : Nodes<{ test: { fields: { id: {}; }; }; }>
51+
>test : Fields<{ fields: { id: {}; }; }>
52+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
interface Entry {
2+
comment?: string;
3+
}
4+
5+
interface Entity {
6+
fields: {[key: string]: Entry};
7+
}
8+
9+
type Fields<E extends Entity> = {
10+
[P in keyof E["fields"]]: E["fields"][P]
11+
};
12+
13+
type Nodes<T = any> = {
14+
[P in keyof T]: T[P] extends Entity
15+
? Fields<T[P]>
16+
: T[P]
17+
};
18+
19+
function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
20+
return {} as Nodes<T>
21+
}
22+
23+
const myTest = makeEntityStore({ test: { fields: { id: {} } } });
24+
myTest.test
25+

0 commit comments

Comments
 (0)