Skip to content

Commit ed795b2

Browse files
authored
Use strict subtype relation in getCommonSupertype (#1288)
1 parent 929d58a commit ed795b2

File tree

4 files changed

+152
-5
lines changed

4 files changed

+152
-5
lines changed

internal/checker/inference.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,11 +1462,7 @@ func (c *Checker) getCommonSupertype(types []*Type) *Type {
14621462
if c.literalTypesWithSameBaseType(primaryTypes) {
14631463
supertype = c.getUnionType(primaryTypes)
14641464
} else {
1465-
for _, t := range primaryTypes {
1466-
if supertype == nil || c.isTypeSubtypeOf(supertype, t) {
1467-
supertype = t
1468-
}
1469-
}
1465+
supertype = c.getSingleCommonSupertype(primaryTypes)
14701466
}
14711467
// Add any nullable types that occurred in the candidates back to the result.
14721468
if core.Same(primaryTypes, types) {
@@ -1475,6 +1471,27 @@ func (c *Checker) getCommonSupertype(types []*Type) *Type {
14751471
return c.getNullableType(supertype, c.getCombinedTypeFlags(types)&TypeFlagsNullable)
14761472
}
14771473

1474+
func (c *Checker) getSingleCommonSupertype(types []*Type) *Type {
1475+
// First, find the leftmost type for which no type to the right is a strict supertype, and if that
1476+
// type is a strict supertype of all other candidates, return it. Otherwise, return the leftmost type
1477+
// for which no type to the right is a (regular) supertype.
1478+
candidate := c.findLeftmostType(types, (*Checker).isTypeStrictSubtypeOf)
1479+
if core.Every(types, func(t *Type) bool { return t == candidate || c.isTypeStrictSubtypeOf(t, candidate) }) {
1480+
return candidate
1481+
}
1482+
return c.findLeftmostType(types, (*Checker).isTypeSubtypeOf)
1483+
}
1484+
1485+
func (c *Checker) findLeftmostType(types []*Type, f func(c *Checker, s *Type, t *Type) bool) *Type {
1486+
var candidate *Type
1487+
for _, t := range types {
1488+
if candidate == nil || f(c, candidate, t) {
1489+
candidate = t
1490+
}
1491+
}
1492+
return candidate
1493+
}
1494+
14781495
// Return the leftmost type for which no type to the right is a subtype.
14791496
func (c *Checker) getCommonSubtype(types []*Type) *Type {
14801497
var subtype *Type
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/strictBestCommonSupertype.ts] ////
2+
3+
=== strictBestCommonSupertype.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1222
5+
6+
class Store<T = object> {
7+
>Store : Symbol(Store, Decl(strictBestCommonSupertype.ts, 0, 0))
8+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 2, 12))
9+
10+
select<K>(mapFn: (state: T) => K) {};
11+
>select : Symbol(select, Decl(strictBestCommonSupertype.ts, 2, 25))
12+
>K : Symbol(K, Decl(strictBestCommonSupertype.ts, 3, 11))
13+
>mapFn : Symbol(mapFn, Decl(strictBestCommonSupertype.ts, 3, 14))
14+
>state : Symbol(state, Decl(strictBestCommonSupertype.ts, 3, 22))
15+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 2, 12))
16+
>K : Symbol(K, Decl(strictBestCommonSupertype.ts, 3, 11))
17+
}
18+
19+
const store: Store = inject(Store);
20+
>store : Symbol(store, Decl(strictBestCommonSupertype.ts, 6, 5))
21+
>Store : Symbol(Store, Decl(strictBestCommonSupertype.ts, 0, 0))
22+
>inject : Symbol(inject, Decl(strictBestCommonSupertype.ts, 6, 35))
23+
>Store : Symbol(Store, Decl(strictBestCommonSupertype.ts, 0, 0))
24+
25+
function inject<T>(token: ProviderToken<T>): T {
26+
>inject : Symbol(inject, Decl(strictBestCommonSupertype.ts, 6, 35))
27+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 8, 16))
28+
>token : Symbol(token, Decl(strictBestCommonSupertype.ts, 8, 19))
29+
>ProviderToken : Symbol(ProviderToken, Decl(strictBestCommonSupertype.ts, 14, 1))
30+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 8, 16))
31+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 8, 16))
32+
33+
return {} as T;
34+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 8, 16))
35+
}
36+
37+
interface Type<T> extends Function {
38+
>Type : Symbol(Type, Decl(strictBestCommonSupertype.ts, 10, 1))
39+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 12, 15))
40+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
41+
42+
new (...args: any[]): T;
43+
>args : Symbol(args, Decl(strictBestCommonSupertype.ts, 13, 9))
44+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 12, 15))
45+
}
46+
47+
type ProviderToken<T> = Type<T> | AbstractType<T>;
48+
>ProviderToken : Symbol(ProviderToken, Decl(strictBestCommonSupertype.ts, 14, 1))
49+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 16, 19))
50+
>Type : Symbol(Type, Decl(strictBestCommonSupertype.ts, 10, 1))
51+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 16, 19))
52+
>AbstractType : Symbol(AbstractType, Decl(strictBestCommonSupertype.ts, 16, 50))
53+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 16, 19))
54+
55+
interface AbstractType<T> extends Function {
56+
>AbstractType : Symbol(AbstractType, Decl(strictBestCommonSupertype.ts, 16, 50))
57+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 18, 23))
58+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
59+
60+
prototype: T;
61+
>prototype : Symbol(prototype, Decl(strictBestCommonSupertype.ts, 18, 44))
62+
>T : Symbol(T, Decl(strictBestCommonSupertype.ts, 18, 23))
63+
}
64+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/strictBestCommonSupertype.ts] ////
2+
3+
=== strictBestCommonSupertype.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1222
5+
6+
class Store<T = object> {
7+
>Store : Store<T>
8+
9+
select<K>(mapFn: (state: T) => K) {};
10+
>select : <K>(mapFn: (state: T) => K) => void
11+
>mapFn : (state: T) => K
12+
>state : T
13+
}
14+
15+
const store: Store = inject(Store);
16+
>store : Store<object>
17+
>inject(Store) : Store<any>
18+
>inject : <T>(token: ProviderToken<T>) => T
19+
>Store : typeof Store
20+
21+
function inject<T>(token: ProviderToken<T>): T {
22+
>inject : <T>(token: ProviderToken<T>) => T
23+
>token : ProviderToken<T>
24+
25+
return {} as T;
26+
>{} as T : T
27+
>{} : {}
28+
}
29+
30+
interface Type<T> extends Function {
31+
new (...args: any[]): T;
32+
>args : any[]
33+
}
34+
35+
type ProviderToken<T> = Type<T> | AbstractType<T>;
36+
>ProviderToken : ProviderToken<T>
37+
38+
interface AbstractType<T> extends Function {
39+
prototype: T;
40+
>prototype : T
41+
}
42+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/typescript-go/issues/1222
5+
6+
class Store<T = object> {
7+
select<K>(mapFn: (state: T) => K) {};
8+
}
9+
10+
const store: Store = inject(Store);
11+
12+
function inject<T>(token: ProviderToken<T>): T {
13+
return {} as T;
14+
}
15+
16+
interface Type<T> extends Function {
17+
new (...args: any[]): T;
18+
}
19+
20+
type ProviderToken<T> = Type<T> | AbstractType<T>;
21+
22+
interface AbstractType<T> extends Function {
23+
prototype: T;
24+
}

0 commit comments

Comments
 (0)