Skip to content

Commit 7b02c63

Browse files
authored
Report generic rest parameters as unreliable variance positions (#33020)
* Report generic rest parameters as unreliable variance positions * Add example from discussion on #30301
1 parent 5e0fbc6 commit 7b02c63

9 files changed

+488
-4
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13919,7 +13919,7 @@ namespace ts {
1391913919
target: Signature,
1392013920
ignoreReturnTypes: boolean): boolean {
1392113921
return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes, /*reportErrors*/ false,
13922-
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
13922+
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
1392313923
}
1392413924

1392513925
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
@@ -13943,7 +13943,8 @@ namespace ts {
1394313943
reportErrors: boolean,
1394413944
errorReporter: ErrorReporter | undefined,
1394513945
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
13946-
compareTypes: TypeComparer): Ternary {
13946+
compareTypes: TypeComparer,
13947+
reportUnreliableMarkers: TypeMapper | undefined): Ternary {
1394713948
// TODO (drosen): De-duplicate code between related functions.
1394813949
if (source === target) {
1394913950
return Ternary.True;
@@ -13966,6 +13967,9 @@ namespace ts {
1396613967
const sourceCount = getParameterCount(source);
1396713968
const sourceRestType = getNonArrayRestType(source);
1396813969
const targetRestType = getNonArrayRestType(target);
13970+
if (sourceRestType || targetRestType) {
13971+
void instantiateType(sourceRestType || targetRestType, reportUnreliableMarkers);
13972+
}
1396913973
if (sourceRestType && targetRestType && sourceCount !== targetCount) {
1397013974
// We're not able to relate misaligned complex rest parameters
1397113975
return Ternary.False;
@@ -14013,7 +14017,7 @@ namespace ts {
1401314017
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
1401414018
const related = callbacks ?
1401514019
// TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
14016-
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes) :
14020+
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
1401714021
!callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
1401814022
if (!related) {
1401914023
if (reportErrors) {
@@ -15967,7 +15971,7 @@ namespace ts {
1596715971
*/
1596815972
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
1596915973
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
15970-
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo);
15974+
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
1597115975
}
1597215976

1597315977
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts]
2+
// the type printback for every `test` below should be "y"
3+
4+
type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
5+
type a = ExtendedMapper<any, any, [any]>;
6+
type b = ExtendedMapper<any, any, any[]>;
7+
type test = a extends b ? "y" : "n"
8+
let check: test = "y";
9+
10+
11+
type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
12+
(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
13+
);
14+
15+
type a1 = ExtendedMapper1<any, any, [any]>;
16+
type b1 = ExtendedMapper1<any, any, any[]>;
17+
type test1 = a1 extends b1 ? "y" : "n"
18+
let check1: test1 = "y";
19+
20+
type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
21+
{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
22+
);
23+
24+
type a2 = ExtendedMapper2<any, any, [any]>;
25+
type b2 = ExtendedMapper2<any, any, any[]>;
26+
type test2 = a2 extends b2 ? "y" : "n"
27+
let check2: test2 = "y";
28+
29+
type a3 = (name: string, mixed: any, args_0: any) => any
30+
type b3 = (name: string, mixed: any, ...args: any[]) => any
31+
32+
type test3 = a3 extends b3 ? "y" : "n"
33+
let check3: test3 = "y";
34+
35+
36+
//// [aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.js]
37+
"use strict";
38+
// the type printback for every `test` below should be "y"
39+
var check = "y";
40+
var check1 = "y";
41+
var check2 = "y";
42+
var check3 = "y";
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
=== tests/cases/compiler/aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts ===
2+
// the type printback for every `test` below should be "y"
3+
4+
type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
5+
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))
6+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 20))
7+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 34))
8+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 43))
9+
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 68))
10+
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 82))
11+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 20))
12+
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 105))
13+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 43))
14+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 34))
15+
16+
type a = ExtendedMapper<any, any, [any]>;
17+
>a : Symbol(a, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 134))
18+
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))
19+
20+
type b = ExtendedMapper<any, any, any[]>;
21+
>b : Symbol(b, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 3, 41))
22+
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))
23+
24+
type test = a extends b ? "y" : "n"
25+
>test : Symbol(test, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 4, 41))
26+
>a : Symbol(a, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 134))
27+
>b : Symbol(b, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 3, 41))
28+
29+
let check: test = "y";
30+
>check : Symbol(check, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 3))
31+
>test : Symbol(test, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 4, 41))
32+
33+
34+
type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
35+
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))
36+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 21))
37+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 35))
38+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 44))
39+
40+
(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
41+
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 5))
42+
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 19))
43+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 21))
44+
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 42))
45+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 44))
46+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 35))
47+
48+
);
49+
50+
type a1 = ExtendedMapper1<any, any, [any]>;
51+
>a1 : Symbol(a1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 11, 2))
52+
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))
53+
54+
type b1 = ExtendedMapper1<any, any, any[]>;
55+
>b1 : Symbol(b1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 13, 43))
56+
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))
57+
58+
type test1 = a1 extends b1 ? "y" : "n"
59+
>test1 : Symbol(test1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 14, 43))
60+
>a1 : Symbol(a1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 11, 2))
61+
>b1 : Symbol(b1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 13, 43))
62+
63+
let check1: test1 = "y";
64+
>check1 : Symbol(check1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 3))
65+
>test1 : Symbol(test1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 14, 43))
66+
67+
type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
68+
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))
69+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 21))
70+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 35))
71+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 44))
72+
73+
{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
74+
>x : Symbol(x, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 5))
75+
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 8))
76+
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 22))
77+
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 21))
78+
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 45))
79+
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 44))
80+
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 35))
81+
82+
);
83+
84+
type a2 = ExtendedMapper2<any, any, [any]>;
85+
>a2 : Symbol(a2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 20, 2))
86+
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))
87+
88+
type b2 = ExtendedMapper2<any, any, any[]>;
89+
>b2 : Symbol(b2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 22, 43))
90+
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))
91+
92+
type test2 = a2 extends b2 ? "y" : "n"
93+
>test2 : Symbol(test2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 23, 43))
94+
>a2 : Symbol(a2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 20, 2))
95+
>b2 : Symbol(b2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 22, 43))
96+
97+
let check2: test2 = "y";
98+
>check2 : Symbol(check2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 3))
99+
>test2 : Symbol(test2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 23, 43))
100+
101+
type a3 = (name: string, mixed: any, args_0: any) => any
102+
>a3 : Symbol(a3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 24))
103+
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 11))
104+
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 24))
105+
>args_0 : Symbol(args_0, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 36))
106+
107+
type b3 = (name: string, mixed: any, ...args: any[]) => any
108+
>b3 : Symbol(b3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 56))
109+
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 11))
110+
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 24))
111+
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 36))
112+
113+
type test3 = a3 extends b3 ? "y" : "n"
114+
>test3 : Symbol(test3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 59))
115+
>a3 : Symbol(a3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 24))
116+
>b3 : Symbol(b3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 56))
117+
118+
let check3: test3 = "y";
119+
>check3 : Symbol(check3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 31, 3))
120+
>test3 : Symbol(test3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 59))
121+
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
=== tests/cases/compiler/aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts ===
2+
// the type printback for every `test` below should be "y"
3+
4+
type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
5+
>ExtendedMapper : ExtendedMapper<HandledInputT, OutputT, ArgsT>
6+
>name : string
7+
>mixed : HandledInputT
8+
>args : ArgsT
9+
10+
type a = ExtendedMapper<any, any, [any]>;
11+
>a : ExtendedMapper<any, any, [any]>
12+
13+
type b = ExtendedMapper<any, any, any[]>;
14+
>b : ExtendedMapper<any, any, any[]>
15+
16+
type test = a extends b ? "y" : "n"
17+
>test : "y"
18+
19+
let check: test = "y";
20+
>check : "y"
21+
>"y" : "y"
22+
23+
24+
type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
25+
>ExtendedMapper1 : ExtendedMapper1<HandledInputT, OutputT, ArgsT>
26+
27+
(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
28+
>name : string
29+
>mixed : HandledInputT
30+
>args : ArgsT
31+
32+
);
33+
34+
type a1 = ExtendedMapper1<any, any, [any]>;
35+
>a1 : ExtendedMapper1<any, any, [any]>
36+
37+
type b1 = ExtendedMapper1<any, any, any[]>;
38+
>b1 : ExtendedMapper1<any, any, any[]>
39+
40+
type test1 = a1 extends b1 ? "y" : "n"
41+
>test1 : "y"
42+
43+
let check1: test1 = "y";
44+
>check1 : "y"
45+
>"y" : "y"
46+
47+
type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
48+
>ExtendedMapper2 : (name: string, mixed: HandledInputT, ...args: ArgsT) => OutputT
49+
50+
{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
51+
>x : (name: string, mixed: HandledInputT, ...args: ArgsT) => OutputT
52+
>name : string
53+
>mixed : HandledInputT
54+
>args : ArgsT
55+
56+
);
57+
58+
type a2 = ExtendedMapper2<any, any, [any]>;
59+
>a2 : (name: string, mixed: any, args_0: any) => any
60+
61+
type b2 = ExtendedMapper2<any, any, any[]>;
62+
>b2 : (name: string, mixed: any, ...args: any[]) => any
63+
64+
type test2 = a2 extends b2 ? "y" : "n"
65+
>test2 : "y"
66+
67+
let check2: test2 = "y";
68+
>check2 : "y"
69+
>"y" : "y"
70+
71+
type a3 = (name: string, mixed: any, args_0: any) => any
72+
>a3 : a3
73+
>name : string
74+
>mixed : any
75+
>args_0 : any
76+
77+
type b3 = (name: string, mixed: any, ...args: any[]) => any
78+
>b3 : b3
79+
>name : string
80+
>mixed : any
81+
>args : any[]
82+
83+
type test3 = a3 extends b3 ? "y" : "n"
84+
>test3 : "y"
85+
86+
let check3: test3 = "y";
87+
>check3 : "y"
88+
>"y" : "y"
89+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//// [lambdaParameterWithTupleArgsHasCorrectAssignability.ts]
2+
type MyTupleItem = {};
3+
type MyTuple = [MyTupleItem, ...MyTupleItem[]];
4+
5+
type GenericFunction<T extends MyTuple> = (...fromArgs: T) => void;
6+
7+
class GenericClass<T extends MyTuple> {
8+
from: GenericFunction<T> | undefined;
9+
}
10+
11+
function createClass<T extends MyTuple>(f: GenericFunction<T>): GenericClass<T> {
12+
return new GenericClass<T>(/* ... use f */);
13+
}
14+
15+
function consumeClass(c: GenericClass<[string, boolean]>) { }
16+
17+
// should work
18+
consumeClass(createClass(str => console.log(str.length)));
19+
20+
// should work
21+
consumeClass(createClass((str, _unused_num) => console.log(str.length)));
22+
23+
24+
//// [lambdaParameterWithTupleArgsHasCorrectAssignability.js]
25+
"use strict";
26+
var GenericClass = /** @class */ (function () {
27+
function GenericClass() {
28+
}
29+
return GenericClass;
30+
}());
31+
function createClass(f) {
32+
return new GenericClass( /* ... use f */);
33+
}
34+
function consumeClass(c) { }
35+
// should work
36+
consumeClass(createClass(function (str) { return console.log(str.length); }));
37+
// should work
38+
consumeClass(createClass(function (str, _unused_num) { return console.log(str.length); }));

0 commit comments

Comments
 (0)