Skip to content

Commit e14c58e

Browse files
committed
Remove comparisons of type parameter defaults, add more test cases
1 parent 40d4f30 commit e14c58e

File tree

6 files changed

+634
-24
lines changed

6 files changed

+634
-24
lines changed

src/compiler/checker.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -9301,12 +9301,17 @@ namespace ts {
93019301
return false;
93029302
}
93039303

9304+
const mapper = createTypeMapper(targetParams, sourceParams);
93049305
for (let i = 0; i < sourceParams.length; i++) {
93059306
const source = sourceParams[i];
93069307
const target = targetParams[i];
93079308
if (source === target) continue;
9308-
if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, getConstraintFromTypeParameter(target) || unknownType)) return false;
9309-
if (!isTypeIdenticalTo(getDefaultFromTypeParameter(source) || unknownType, getDefaultFromTypeParameter(target) || unknownType)) return false;
9309+
// We instantiate the target type parameter constraints into the source types so we can recognize `<T, U extends T>` as the same as `<A, B extends A>`
9310+
if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false;
9311+
// We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match.
9312+
// It might make sense to combine these defaults in the future, but doing so intelligently requires knowing
9313+
// if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type)
9314+
// and, since it's just an inference _default_, just picking one arbitrarily works OK.
93109315
}
93119316

93129317
return true;
@@ -9374,6 +9379,7 @@ namespace ts {
93749379
let paramMapper: TypeMapper | undefined;
93759380
if (left.typeParameters && right.typeParameters) {
93769381
paramMapper = createTypeMapper(right.typeParameters, left.typeParameters);
9382+
// We just use the type parameter defaults from the first signature
93779383
}
93789384
const declaration = left.declaration;
93799385
const params = combineUnionParameters(left, right, paramMapper);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
tests/cases/compiler/unionOfClassCalls.ts(28,5): error TS2349: This expression is not callable.
2+
Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other.
3+
4+
5+
==== tests/cases/compiler/unionOfClassCalls.ts (1 errors) ====
6+
// from https://github.com/microsoft/TypeScript/issues/30717
7+
declare class Test<T> {
8+
obj: T;
9+
get<K extends keyof T>(k: K): T[K];
10+
}
11+
12+
interface A { t: "A" }
13+
interface B { t: "B" }
14+
15+
declare const tmp: Test<A> | Test<B>;
16+
17+
switch (tmp.get('t')) {
18+
case 'A': break;
19+
case 'B': break;
20+
}
21+
22+
// from https://github.com/microsoft/TypeScript/issues/36390
23+
24+
const arr: number[] | string[] = []; // Works with Array<number | string>
25+
const arr1: number[] = [];
26+
const arr2: string[] = [];
27+
28+
arr.map((a: number | string, index: number) => {
29+
return index
30+
})
31+
32+
// This case still doesn't work because `reduce` has multiple overloads :(
33+
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
34+
~~~~~~
35+
!!! error TS2349: This expression is not callable.
36+
!!! error TS2349: Each member of the union type '{ (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; (callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; <U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; } | { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; }' has signatures, but none of those signatures are compatible with each other.
37+
return []
38+
}, [])
39+
40+
arr.forEach((a: number | string, index: number) => {
41+
return index
42+
})
43+
44+
arr1.map((a: number, index: number) => {
45+
return index
46+
})
47+
48+
arr1.reduce((acc: number[], a: number, index: number) => {
49+
return [a]
50+
}, [])
51+
52+
arr1.forEach((a: number, index: number) => {
53+
return index
54+
})
55+
arr2.map((a: string, index: number) => {
56+
return index
57+
})
58+
59+
arr2.reduce((acc: string[], a: string, index: number) => {
60+
return []
61+
}, [])
62+
63+
arr2.forEach((a: string, index: number) => {
64+
return index
65+
})
66+
67+
// from https://github.com/microsoft/TypeScript/issues/36307
68+
69+
declare class Foo {
70+
doThing(): Promise<this>
71+
}
72+
73+
declare class Bar extends Foo {
74+
bar: number;
75+
}
76+
declare class Baz extends Foo {
77+
baz: number;
78+
}
79+
80+
declare var a: Bar | Baz;
81+
// note, you must annotate `result` for now
82+
a.doThing().then((result: Bar | Baz) => {
83+
// whatever
84+
});
85+

tests/baselines/reference/unionOfClassCalls.js

+101-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//// [unionOfClassCalls.ts]
2+
// from https://github.com/microsoft/TypeScript/issues/30717
23
declare class Test<T> {
34
obj: T;
45
get<K extends keyof T>(k: K): T[K];
@@ -12,10 +13,109 @@ declare const tmp: Test<A> | Test<B>;
1213
switch (tmp.get('t')) {
1314
case 'A': break;
1415
case 'B': break;
15-
}
16+
}
17+
18+
// from https://github.com/microsoft/TypeScript/issues/36390
19+
20+
const arr: number[] | string[] = []; // Works with Array<number | string>
21+
const arr1: number[] = [];
22+
const arr2: string[] = [];
23+
24+
arr.map((a: number | string, index: number) => {
25+
return index
26+
})
27+
28+
// This case still doesn't work because `reduce` has multiple overloads :(
29+
arr.reduce((acc: Array<string>, a: number | string, index: number) => {
30+
return []
31+
}, [])
32+
33+
arr.forEach((a: number | string, index: number) => {
34+
return index
35+
})
36+
37+
arr1.map((a: number, index: number) => {
38+
return index
39+
})
40+
41+
arr1.reduce((acc: number[], a: number, index: number) => {
42+
return [a]
43+
}, [])
44+
45+
arr1.forEach((a: number, index: number) => {
46+
return index
47+
})
48+
arr2.map((a: string, index: number) => {
49+
return index
50+
})
51+
52+
arr2.reduce((acc: string[], a: string, index: number) => {
53+
return []
54+
}, [])
55+
56+
arr2.forEach((a: string, index: number) => {
57+
return index
58+
})
59+
60+
// from https://github.com/microsoft/TypeScript/issues/36307
61+
62+
declare class Foo {
63+
doThing(): Promise<this>
64+
}
65+
66+
declare class Bar extends Foo {
67+
bar: number;
68+
}
69+
declare class Baz extends Foo {
70+
baz: number;
71+
}
72+
73+
declare var a: Bar | Baz;
74+
// note, you must annotate `result` for now
75+
a.doThing().then((result: Bar | Baz) => {
76+
// whatever
77+
});
78+
1679

1780
//// [unionOfClassCalls.js]
81+
"use strict";
1882
switch (tmp.get('t')) {
1983
case 'A': break;
2084
case 'B': break;
2185
}
86+
// from https://github.com/microsoft/TypeScript/issues/36390
87+
var arr = []; // Works with Array<number | string>
88+
var arr1 = [];
89+
var arr2 = [];
90+
arr.map(function (a, index) {
91+
return index;
92+
});
93+
// This case still doesn't work because `reduce` has multiple overloads :(
94+
arr.reduce(function (acc, a, index) {
95+
return [];
96+
}, []);
97+
arr.forEach(function (a, index) {
98+
return index;
99+
});
100+
arr1.map(function (a, index) {
101+
return index;
102+
});
103+
arr1.reduce(function (acc, a, index) {
104+
return [a];
105+
}, []);
106+
arr1.forEach(function (a, index) {
107+
return index;
108+
});
109+
arr2.map(function (a, index) {
110+
return index;
111+
});
112+
arr2.reduce(function (acc, a, index) {
113+
return [];
114+
}, []);
115+
arr2.forEach(function (a, index) {
116+
return index;
117+
});
118+
// note, you must annotate `result` for now
119+
a.doThing().then(function (result) {
120+
// whatever
121+
});

0 commit comments

Comments
 (0)