Skip to content

Commit c5d21e7

Browse files
authored
Lower priority for speculative variadic tuple inferences (microsoft#39723)
* Inference to [...T, X?] has lower priority than inference to [...T, X] * Update tests * Accept new API baselines
1 parent 823b69a commit c5d21e7

File tree

9 files changed

+630
-473
lines changed

9 files changed

+630
-473
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18531,8 +18531,8 @@ namespace ts {
1853118531
return restType && createArrayType(restType);
1853218532
}
1853318533

18534-
function getEndLengthOfType(type: Type, flags: ElementFlags) {
18535-
return isTupleType(type) ? getTypeReferenceArity(type) - findLastIndex(type.target.elementFlags, f => !(f & flags)) - 1 : 0;
18534+
function getEndLengthOfType(type: Type) {
18535+
return isTupleType(type) ? getTypeReferenceArity(type) - findLastIndex(type.target.elementFlags, f => !(f & (ElementFlags.Required | ElementFlags.Optional))) - 1 : 0;
1853618536
}
1853718537

1853818538
function getElementTypeOfSliceOfTupleType(type: TupleTypeReference, index: number, endSkipCount = 0, writing = false) {
@@ -19793,8 +19793,8 @@ namespace ts {
1979319793
const sourceRestType = !isTupleType(source) || sourceArity > 0 && source.target.elementFlags[sourceArity - 1] & ElementFlags.Rest ?
1979419794
getTypeArguments(source)[sourceArity - 1] : undefined;
1979519795
const endLength = !(target.target.combinedFlags & ElementFlags.Variable) ? 0 :
19796-
sourceRestType ? getEndLengthOfType(target, ElementFlags.Required) :
19797-
Math.min(getEndLengthOfType(source, ElementFlags.Required | ElementFlags.Optional), getEndLengthOfType(target, ElementFlags.Required));
19796+
sourceRestType ? getEndLengthOfType(target) :
19797+
Math.min(getEndLengthOfType(source), getEndLengthOfType(target));
1979819798
const sourceEndLength = sourceRestType ? 0 : endLength;
1979919799
// Infer between starting fixed elements.
1980019800
for (let i = 0; i < startLength; i++) {
@@ -19819,7 +19819,10 @@ namespace ts {
1981919819
}
1982019820
else if (middleLength === 1 && elementFlags[startLength] & ElementFlags.Variadic) {
1982119821
// Middle of target is exactly one variadic element. Infer the slice between the fixed parts in the source.
19822-
inferFromTypes(isTupleType(source) ? sliceTupleType(source, startLength, sourceEndLength) : createArrayType(sourceRestType!), elementTypes[startLength]);
19822+
// If target ends in optional element(s), make a lower priority a speculative inference.
19823+
const endsInOptional = target.target.elementFlags[targetArity - 1] & ElementFlags.Optional;
19824+
const sourceSlice = isTupleType(source) ? sliceTupleType(source, startLength, sourceEndLength) : createArrayType(sourceRestType!);
19825+
inferWithPriority(sourceSlice, elementTypes[startLength], endsInOptional ? InferencePriority.SpeculativeTuple : 0);
1982319826
}
1982419827
else if (middleLength === 1 && elementFlags[startLength] & ElementFlags.Rest) {
1982519828
// Middle of target is exactly one rest element. If middle of source is not empty, infer union of middle element types.

src/compiler/types.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5424,15 +5424,16 @@ namespace ts {
54245424

54255425
export const enum InferencePriority {
54265426
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
5427-
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
5428-
PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type
5429-
MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type
5430-
ContravariantConditional = 1 << 4, // Conditional type in contravariant position
5431-
ReturnType = 1 << 5, // Inference made from return type of generic function
5432-
LiteralKeyof = 1 << 6, // Inference made from a string literal to a keyof T
5433-
NoConstraints = 1 << 7, // Don't infer from constraints of instantiable types
5434-
AlwaysStrict = 1 << 8, // Always use strict rules for contravariant inferences
5435-
MaxValue = 1 << 9, // Seed for inference priority tracking
5427+
SpeculativeTuple = 1 << 1, // Speculative tuple inference
5428+
HomomorphicMappedType = 1 << 2, // Reverse inference for homomorphic mapped type
5429+
PartialHomomorphicMappedType = 1 << 3, // Partial reverse inference for homomorphic mapped type
5430+
MappedTypeConstraint = 1 << 4, // Reverse inference for mapped type
5431+
ContravariantConditional = 1 << 5, // Conditional type in contravariant position
5432+
ReturnType = 1 << 6, // Inference made from return type of generic function
5433+
LiteralKeyof = 1 << 7, // Inference made from a string literal to a keyof T
5434+
NoConstraints = 1 << 8, // Don't infer from constraints of instantiable types
5435+
AlwaysStrict = 1 << 9, // Always use strict rules for contravariant inferences
5436+
MaxValue = 1 << 10, // Seed for inference priority tracking
54365437

54375438
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
54385439
Circularity = -1, // Inference circularity (value less than all other priorities)

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,16 +2645,17 @@ declare namespace ts {
26452645
}
26462646
export enum InferencePriority {
26472647
NakedTypeVariable = 1,
2648-
HomomorphicMappedType = 2,
2649-
PartialHomomorphicMappedType = 4,
2650-
MappedTypeConstraint = 8,
2651-
ContravariantConditional = 16,
2652-
ReturnType = 32,
2653-
LiteralKeyof = 64,
2654-
NoConstraints = 128,
2655-
AlwaysStrict = 256,
2656-
MaxValue = 512,
2657-
PriorityImpliesCombination = 104,
2648+
SpeculativeTuple = 2,
2649+
HomomorphicMappedType = 4,
2650+
PartialHomomorphicMappedType = 8,
2651+
MappedTypeConstraint = 16,
2652+
ContravariantConditional = 32,
2653+
ReturnType = 64,
2654+
LiteralKeyof = 128,
2655+
NoConstraints = 256,
2656+
AlwaysStrict = 512,
2657+
MaxValue = 1024,
2658+
PriorityImpliesCombination = 208,
26582659
Circularity = -1
26592660
}
26602661
/** @deprecated Use FileExtensionInfo instead. */

tests/baselines/reference/api/typescript.d.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,16 +2645,17 @@ declare namespace ts {
26452645
}
26462646
export enum InferencePriority {
26472647
NakedTypeVariable = 1,
2648-
HomomorphicMappedType = 2,
2649-
PartialHomomorphicMappedType = 4,
2650-
MappedTypeConstraint = 8,
2651-
ContravariantConditional = 16,
2652-
ReturnType = 32,
2653-
LiteralKeyof = 64,
2654-
NoConstraints = 128,
2655-
AlwaysStrict = 256,
2656-
MaxValue = 512,
2657-
PriorityImpliesCombination = 104,
2648+
SpeculativeTuple = 2,
2649+
HomomorphicMappedType = 4,
2650+
PartialHomomorphicMappedType = 8,
2651+
MappedTypeConstraint = 16,
2652+
ContravariantConditional = 32,
2653+
ReturnType = 64,
2654+
LiteralKeyof = 128,
2655+
NoConstraints = 256,
2656+
AlwaysStrict = 512,
2657+
MaxValue = 1024,
2658+
PriorityImpliesCombination = 208,
26582659
Circularity = -1
26592660
}
26602661
/** @deprecated Use FileExtensionInfo instead. */

tests/baselines/reference/variadicTuples1.errors.txt

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(191,5): error TS2322: Typ
3939
'U' could be instantiated with an arbitrary type which could be unrelated to 'readonly string[]'.
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"'.
42-
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'.
42+
tests/cases/conformance/types/tuple/variadicTuples1.ts(346,14): error TS7019: Rest parameter 'x' implicitly has an 'any[]' type.
43+
tests/cases/conformance/types/tuple/variadicTuples1.ts(354,26): error TS2322: Type 'string' is not assignable to type 'number | undefined'.
4544

4645

47-
==== tests/cases/conformance/types/tuple/variadicTuples1.ts (21 errors) ====
46+
==== tests/cases/conformance/types/tuple/variadicTuples1.ts (20 errors) ====
4847
// Variadics in tuple types
4948

5049
type TV0<T extends unknown[]> = [string, ...T];
@@ -313,50 +312,54 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(342,19): error TS2322: Ty
313312
// Inference between variadic tuple types
314313

315314
type First<T extends readonly unknown[]> = T[0];
316-
type DropFirst<T extends readonly unknown[]> = T extends readonly [any, ...infer U] ? U : [...T];
315+
type DropFirst<T extends readonly unknown[]> = T extends readonly [any?, ...infer U] ? U : [...T];
317316

318-
type Last<T extends readonly unknown[]> = T extends readonly [...infer _, infer U] ? U : undefined;
319-
type DropLast<T extends readonly unknown[]> = T extends readonly [...infer U, any] ? U : [...T];
317+
type Last<T extends readonly unknown[]> = T extends readonly [...infer _, infer U] ? U : T extends readonly [...infer _, (infer U)?] ? U | undefined : undefined;
318+
type DropLast<T extends readonly unknown[]> = T extends readonly [...infer U, any?] ? U : [...T];
320319

321320
type T00 = First<[number, symbol, string]>;
322321
type T01 = First<[symbol, string]>;
323322
type T02 = First<[string]>;
324323
type T03 = First<[number, symbol, ...string[]]>;
325324
type T04 = First<[symbol, ...string[]]>;
326-
type T05 = First<string[]>;
327-
type T06 = First<[]>;
328-
type T07 = First<any>;
329-
type T08 = First<never>;
325+
type T05 = First<[string?]>;
326+
type T06 = First<string[]>;
327+
type T07 = First<[]>;
328+
type T08 = First<any>;
329+
type T09 = First<never>;
330330

331331
type T10 = DropFirst<[number, symbol, string]>;
332332
type T11 = DropFirst<[symbol, string]>;
333333
type T12 = DropFirst<[string]>;
334334
type T13 = DropFirst<[number, symbol, ...string[]]>;
335335
type T14 = DropFirst<[symbol, ...string[]]>;
336-
type T15 = DropFirst<string[]>;
337-
type T16 = DropFirst<[]>;
338-
type T17 = DropFirst<any>;
339-
type T18 = DropFirst<never>;
336+
type T15 = DropFirst<[string?]>;
337+
type T16 = DropFirst<string[]>;
338+
type T17 = DropFirst<[]>;
339+
type T18 = DropFirst<any>;
340+
type T19 = DropFirst<never>;
340341

341342
type T20 = Last<[number, symbol, string]>;
342343
type T21 = Last<[symbol, string]>;
343344
type T22 = Last<[string]>;
344345
type T23 = Last<[number, symbol, ...string[]]>;
345346
type T24 = Last<[symbol, ...string[]]>;
346-
type T25 = Last<string[]>;
347-
type T26 = Last<[]>; // unknown[], maybe should be []
348-
type T27 = Last<any>; // unknown, maybe should be any
349-
type T28 = Last<never>;
347+
type T25 = Last<[string?]>;
348+
type T26 = Last<string[]>;
349+
type T27 = Last<[]>; // unknown, maybe should undefined
350+
type T28 = Last<any>; // unknown, maybe should be any
351+
type T29 = Last<never>;
350352

351353
type T30 = DropLast<[number, symbol, string]>;
352354
type T31 = DropLast<[symbol, string]>;
353355
type T32 = DropLast<[string]>;
354356
type T33 = DropLast<[number, symbol, ...string[]]>;
355357
type T34 = DropLast<[symbol, ...string[]]>;
356-
type T35 = DropLast<string[]>;
357-
type T36 = DropLast<[]>; // unknown[], maybe should be []
358-
type T37 = DropLast<any>;
359-
type T38 = DropLast<never>;
358+
type T35 = DropLast<[string?]>;
359+
type T36 = DropLast<string[]>;
360+
type T37 = DropLast<[]>; // unknown[], maybe should be []
361+
type T38 = DropLast<any>;
362+
type T39 = DropLast<never>;
360363

361364
type R00 = First<readonly [number, symbol, string]>;
362365
type R01 = First<readonly [symbol, string]>;
@@ -428,6 +431,15 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(342,19): error TS2322: Ty
428431
curry2(fn10, ['hello', 42], [true]);
429432
curry2(fn10, ['hello'], [42, true]);
430433

434+
// Inference to [...T] has higher priority than inference to [...T, number?]
435+
436+
declare function ft<T extends unknown[]>(t1: [...T], t2: [...T, number?]): T;
437+
438+
ft([1, 2, 3], [1, 2, 3]);
439+
ft([1, 2], [1, 2, 3]);
440+
ft(['a', 'b'], ['c', 'd'])
441+
ft(['a', 'b'], ['c', 'd', 42])
442+
431443
// Last argument is contextually typed
432444

433445
declare function call<T extends unknown[], R>(...args: [...T, (...args: T) => R]): [T, R];
@@ -447,12 +459,10 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(342,19): error TS2322: Ty
447459

448460
function f21<U extends string[]>(args: [...U, number?]) {
449461
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-
~~~~~
462+
let v2 = f20(["foo", "bar"]); // [string]
463+
~~~~~
455464
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
465+
let v3 = f20(["foo", 42]); // [string]
456466
}
457467

458468
declare function f22<T extends unknown[] = []>(args: [...T, number]): T;

0 commit comments

Comments
 (0)