Skip to content

Commit b448540

Browse files
authored
No speculative inferences for variadic tuples (microsoft#39281)
* No speculative inferences to types like [...T, U?] * Add tests
1 parent 57dd722 commit b448540

File tree

6 files changed

+211
-2
lines changed

6 files changed

+211
-2
lines changed

src/compiler/checker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18462,7 +18462,7 @@ namespace ts {
1846218462
}
1846318463

1846418464
function getEndLengthOfType(type: Type) {
18465-
return isTupleType(type) ? getTypeReferenceArity(type) - findLastIndex(type.target.elementFlags, f => !!(f & ElementFlags.Variable)) - 1 : 0;
18465+
return isTupleType(type) ? getTypeReferenceArity(type) - findLastIndex(type.target.elementFlags, f => !(f & ElementFlags.Required)) - 1 : 0;
1846618466
}
1846718467

1846818468
function getElementTypeOfSliceOfTupleType(type: TupleTypeReference, index: number, endSkipCount = 0, writing = false) {

tests/baselines/reference/variadicTuples1.errors.txt

+26-1
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(191,5): error TS2322: Typ
4040
tests/cases/conformance/types/tuple/variadicTuples1.ts(203,5): error TS2322: Type 'string' is not assignable to type 'keyof [1, 2, ...T]'.
4141
Type '"2"' is not assignable to type 'number | "0" | "length" | "toString" | "toLocaleString" | "pop" | "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight" | "1"'.
4242
tests/cases/conformance/types/tuple/variadicTuples1.ts(333,14): error TS7019: Rest parameter 'x' implicitly has an 'any[]' type.
43+
tests/cases/conformance/types/tuple/variadicTuples1.ts(341,19): error TS2322: Type 'string' is not assignable to type 'number | undefined'.
44+
tests/cases/conformance/types/tuple/variadicTuples1.ts(342,19): error TS2322: Type 'string' is not assignable to type 'number | undefined'.
4345

4446

45-
==== tests/cases/conformance/types/tuple/variadicTuples1.ts (19 errors) ====
47+
==== tests/cases/conformance/types/tuple/variadicTuples1.ts (21 errors) ====
4648
// Variadics in tuple types
4749

4850
type TV0<T extends unknown[]> = [string, ...T];
@@ -438,4 +440,27 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(333,14): error TS7019: Re
438440
call(...sa, (...x) => 42);
439441
~~~~
440442
!!! error TS7019: Rest parameter 'x' implicitly has an 'any[]' type.
443+
444+
// No inference to ending optional elements (except with identical structure)
445+
446+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
447+
448+
function f21<U extends string[]>(args: [...U, number?]) {
449+
let v1 = f20(args); // U
450+
let v2 = f20(["foo", "bar"]); // []
451+
~~~~~
452+
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
453+
let v3 = f20(["foo", 42]); // []
454+
~~~~~
455+
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
456+
}
457+
458+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
459+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
460+
461+
function f23<U extends string[]>(args: [...U, number]) {
462+
let v1 = f22(args); // U
463+
let v2 = f22(["foo", "bar"]); // [string, string]
464+
let v3 = f22(["foo", 42]); // [string]
465+
}
441466

tests/baselines/reference/variadicTuples1.js

+34
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,25 @@ call('hello', 32, (a, b) => 42);
332332
// Requires [starting-fixed-part, ...rest-part, ending-fixed-part] tuple structure
333333

334334
call(...sa, (...x) => 42);
335+
336+
// No inference to ending optional elements (except with identical structure)
337+
338+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
339+
340+
function f21<U extends string[]>(args: [...U, number?]) {
341+
let v1 = f20(args); // U
342+
let v2 = f20(["foo", "bar"]); // []
343+
let v3 = f20(["foo", 42]); // []
344+
}
345+
346+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
347+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
348+
349+
function f23<U extends string[]>(args: [...U, number]) {
350+
let v1 = f22(args); // U
351+
let v2 = f22(["foo", "bar"]); // [string, string]
352+
let v3 = f22(["foo", 42]); // [string]
353+
}
335354

336355

337356
//// [variadicTuples1.js]
@@ -528,6 +547,16 @@ call.apply(void 0, __spreadArrays(sa, [function () {
528547
}
529548
return 42;
530549
}]));
550+
function f21(args) {
551+
var v1 = f20(args); // U
552+
var v2 = f20(["foo", "bar"]); // []
553+
var v3 = f20(["foo", 42]); // []
554+
}
555+
function f23(args) {
556+
var v1 = f22(args); // U
557+
var v2 = f22(["foo", "bar"]); // [string, string]
558+
var v3 = f22(["foo", 42]); // [string]
559+
}
531560

532561

533562
//// [variadicTuples1.d.ts]
@@ -669,3 +698,8 @@ declare const c22: (...b: string[]) => number;
669698
declare function curry2<T extends unknown[], U extends unknown[], R>(f: (...args: [...T, ...U]) => R, t: [...T], u: [...U]): R;
670699
declare function fn10(a: string, b: number, c: boolean): string[];
671700
declare function call<T extends unknown[], R>(...args: [...T, (...args: T) => R]): [T, R];
701+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
702+
declare function f21<U extends string[]>(args: [...U, number?]): void;
703+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
704+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
705+
declare function f23<U extends string[]>(args: [...U, number]): void;

tests/baselines/reference/variadicTuples1.symbols

+63
Original file line numberDiff line numberDiff line change
@@ -1141,3 +1141,66 @@ call(...sa, (...x) => 42);
11411141
>sa : Symbol(sa, Decl(variadicTuples1.ts, 29, 13))
11421142
>x : Symbol(x, Decl(variadicTuples1.ts, 332, 13))
11431143

1144+
// No inference to ending optional elements (except with identical structure)
1145+
1146+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
1147+
>f20 : Symbol(f20, Decl(variadicTuples1.ts, 332, 26))
1148+
>T : Symbol(T, Decl(variadicTuples1.ts, 336, 21))
1149+
>args : Symbol(args, Decl(variadicTuples1.ts, 336, 47))
1150+
>T : Symbol(T, Decl(variadicTuples1.ts, 336, 21))
1151+
>T : Symbol(T, Decl(variadicTuples1.ts, 336, 21))
1152+
1153+
function f21<U extends string[]>(args: [...U, number?]) {
1154+
>f21 : Symbol(f21, Decl(variadicTuples1.ts, 336, 73))
1155+
>U : Symbol(U, Decl(variadicTuples1.ts, 338, 13))
1156+
>args : Symbol(args, Decl(variadicTuples1.ts, 338, 33))
1157+
>U : Symbol(U, Decl(variadicTuples1.ts, 338, 13))
1158+
1159+
let v1 = f20(args); // U
1160+
>v1 : Symbol(v1, Decl(variadicTuples1.ts, 339, 7))
1161+
>f20 : Symbol(f20, Decl(variadicTuples1.ts, 332, 26))
1162+
>args : Symbol(args, Decl(variadicTuples1.ts, 338, 33))
1163+
1164+
let v2 = f20(["foo", "bar"]); // []
1165+
>v2 : Symbol(v2, Decl(variadicTuples1.ts, 340, 7))
1166+
>f20 : Symbol(f20, Decl(variadicTuples1.ts, 332, 26))
1167+
1168+
let v3 = f20(["foo", 42]); // []
1169+
>v3 : Symbol(v3, Decl(variadicTuples1.ts, 341, 7))
1170+
>f20 : Symbol(f20, Decl(variadicTuples1.ts, 332, 26))
1171+
}
1172+
1173+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
1174+
>f22 : Symbol(f22, Decl(variadicTuples1.ts, 342, 1), Decl(variadicTuples1.ts, 344, 72))
1175+
>T : Symbol(T, Decl(variadicTuples1.ts, 344, 21))
1176+
>args : Symbol(args, Decl(variadicTuples1.ts, 344, 47))
1177+
>T : Symbol(T, Decl(variadicTuples1.ts, 344, 21))
1178+
>T : Symbol(T, Decl(variadicTuples1.ts, 344, 21))
1179+
1180+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
1181+
>f22 : Symbol(f22, Decl(variadicTuples1.ts, 342, 1), Decl(variadicTuples1.ts, 344, 72))
1182+
>T : Symbol(T, Decl(variadicTuples1.ts, 345, 21))
1183+
>args : Symbol(args, Decl(variadicTuples1.ts, 345, 47))
1184+
>T : Symbol(T, Decl(variadicTuples1.ts, 345, 21))
1185+
>T : Symbol(T, Decl(variadicTuples1.ts, 345, 21))
1186+
1187+
function f23<U extends string[]>(args: [...U, number]) {
1188+
>f23 : Symbol(f23, Decl(variadicTuples1.ts, 345, 64))
1189+
>U : Symbol(U, Decl(variadicTuples1.ts, 347, 13))
1190+
>args : Symbol(args, Decl(variadicTuples1.ts, 347, 33))
1191+
>U : Symbol(U, Decl(variadicTuples1.ts, 347, 13))
1192+
1193+
let v1 = f22(args); // U
1194+
>v1 : Symbol(v1, Decl(variadicTuples1.ts, 348, 7))
1195+
>f22 : Symbol(f22, Decl(variadicTuples1.ts, 342, 1), Decl(variadicTuples1.ts, 344, 72))
1196+
>args : Symbol(args, Decl(variadicTuples1.ts, 347, 33))
1197+
1198+
let v2 = f22(["foo", "bar"]); // [string, string]
1199+
>v2 : Symbol(v2, Decl(variadicTuples1.ts, 349, 7))
1200+
>f22 : Symbol(f22, Decl(variadicTuples1.ts, 342, 1), Decl(variadicTuples1.ts, 344, 72))
1201+
1202+
let v3 = f22(["foo", 42]); // [string]
1203+
>v3 : Symbol(v3, Decl(variadicTuples1.ts, 350, 7))
1204+
>f22 : Symbol(f22, Decl(variadicTuples1.ts, 342, 1), Decl(variadicTuples1.ts, 344, 72))
1205+
}
1206+

tests/baselines/reference/variadicTuples1.types

+68
Original file line numberDiff line numberDiff line change
@@ -1186,3 +1186,71 @@ call(...sa, (...x) => 42);
11861186
>x : any[]
11871187
>42 : 42
11881188

1189+
// No inference to ending optional elements (except with identical structure)
1190+
1191+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
1192+
>f20 : <T extends unknown[] = []>(args: [...T, number?]) => T
1193+
>args : [...T, (number | undefined)?]
1194+
1195+
function f21<U extends string[]>(args: [...U, number?]) {
1196+
>f21 : <U extends string[]>(args: [...U, number?]) => void
1197+
>args : [...U, (number | undefined)?]
1198+
1199+
let v1 = f20(args); // U
1200+
>v1 : U
1201+
>f20(args) : U
1202+
>f20 : <T extends unknown[] = []>(args: [...T, (number | undefined)?]) => T
1203+
>args : [...U, (number | undefined)?]
1204+
1205+
let v2 = f20(["foo", "bar"]); // []
1206+
>v2 : []
1207+
>f20(["foo", "bar"]) : []
1208+
>f20 : <T extends unknown[] = []>(args: [...T, (number | undefined)?]) => T
1209+
>["foo", "bar"] : [string, string]
1210+
>"foo" : "foo"
1211+
>"bar" : "bar"
1212+
1213+
let v3 = f20(["foo", 42]); // []
1214+
>v3 : []
1215+
>f20(["foo", 42]) : []
1216+
>f20 : <T extends unknown[] = []>(args: [...T, (number | undefined)?]) => T
1217+
>["foo", 42] : [string, number]
1218+
>"foo" : "foo"
1219+
>42 : 42
1220+
}
1221+
1222+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
1223+
>f22 : { <T extends unknown[] = []>(args: [...T, number]): T; <T extends unknown[] = []>(args: [...T]): T; }
1224+
>args : [...T, number]
1225+
1226+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
1227+
>f22 : { <T extends unknown[] = []>(args: [...T, number]): T; <T extends unknown[] = []>(args: [...T]): T; }
1228+
>args : [...T]
1229+
1230+
function f23<U extends string[]>(args: [...U, number]) {
1231+
>f23 : <U extends string[]>(args: [...U, number]) => void
1232+
>args : [...U, number]
1233+
1234+
let v1 = f22(args); // U
1235+
>v1 : U
1236+
>f22(args) : U
1237+
>f22 : { <T extends unknown[] = []>(args: [...T, number]): T; <T extends unknown[] = []>(args: [...T]): T; }
1238+
>args : [...U, number]
1239+
1240+
let v2 = f22(["foo", "bar"]); // [string, string]
1241+
>v2 : [string, string]
1242+
>f22(["foo", "bar"]) : [string, string]
1243+
>f22 : { <T extends unknown[] = []>(args: [...T, number]): T; <T extends unknown[] = []>(args: [...T]): T; }
1244+
>["foo", "bar"] : [string, string]
1245+
>"foo" : "foo"
1246+
>"bar" : "bar"
1247+
1248+
let v3 = f22(["foo", 42]); // [string]
1249+
>v3 : [string]
1250+
>f22(["foo", 42]) : [string]
1251+
>f22 : { <T extends unknown[] = []>(args: [...T, number]): T; <T extends unknown[] = []>(args: [...T]): T; }
1252+
>["foo", 42] : [string, number]
1253+
>"foo" : "foo"
1254+
>42 : 42
1255+
}
1256+

tests/cases/conformance/types/tuple/variadicTuples1.ts

+19
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,22 @@ call('hello', 32, (a, b) => 42);
334334
// Requires [starting-fixed-part, ...rest-part, ending-fixed-part] tuple structure
335335

336336
call(...sa, (...x) => 42);
337+
338+
// No inference to ending optional elements (except with identical structure)
339+
340+
declare function f20<T extends unknown[] = []>(args: [...T, number?]): T;
341+
342+
function f21<U extends string[]>(args: [...U, number?]) {
343+
let v1 = f20(args); // U
344+
let v2 = f20(["foo", "bar"]); // []
345+
let v3 = f20(["foo", 42]); // []
346+
}
347+
348+
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;
349+
declare function f22<T extends unknown[] = []>(args: [...T]): T;
350+
351+
function f23<U extends string[]>(args: [...U, number]) {
352+
let v1 = f22(args); // U
353+
let v2 = f22(["foo", "bar"]); // [string, string]
354+
let v3 = f22(["foo", 42]); // [string]
355+
}

0 commit comments

Comments
 (0)