Skip to content

Lower priority for speculative variadic tuple inferences #39723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18531,8 +18531,8 @@ namespace ts {
return restType && createArrayType(restType);
}

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

function getElementTypeOfSliceOfTupleType(type: TupleTypeReference, index: number, endSkipCount = 0, writing = false) {
Expand Down Expand Up @@ -19793,8 +19793,8 @@ namespace ts {
const sourceRestType = !isTupleType(source) || sourceArity > 0 && source.target.elementFlags[sourceArity - 1] & ElementFlags.Rest ?
getTypeArguments(source)[sourceArity - 1] : undefined;
const endLength = !(target.target.combinedFlags & ElementFlags.Variable) ? 0 :
sourceRestType ? getEndLengthOfType(target, ElementFlags.Required) :
Math.min(getEndLengthOfType(source, ElementFlags.Required | ElementFlags.Optional), getEndLengthOfType(target, ElementFlags.Required));
sourceRestType ? getEndLengthOfType(target) :
Math.min(getEndLengthOfType(source), getEndLengthOfType(target));
const sourceEndLength = sourceRestType ? 0 : endLength;
// Infer between starting fixed elements.
for (let i = 0; i < startLength; i++) {
Expand All @@ -19819,7 +19819,10 @@ namespace ts {
}
else if (middleLength === 1 && elementFlags[startLength] & ElementFlags.Variadic) {
// Middle of target is exactly one variadic element. Infer the slice between the fixed parts in the source.
inferFromTypes(isTupleType(source) ? sliceTupleType(source, startLength, sourceEndLength) : createArrayType(sourceRestType!), elementTypes[startLength]);
// If target ends in optional element(s), make a lower priority a speculative inference.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the documentation is going to be sparse on SpeculativeTuple, I think showing a concrete example here is worthwhile. Your explanation on the PR would be great.

const endsInOptional = target.target.elementFlags[targetArity - 1] & ElementFlags.Optional;
const sourceSlice = isTupleType(source) ? sliceTupleType(source, startLength, sourceEndLength) : createArrayType(sourceRestType!);
inferWithPriority(sourceSlice, elementTypes[startLength], endsInOptional ? InferencePriority.SpeculativeTuple : 0);
}
else if (middleLength === 1 && elementFlags[startLength] & ElementFlags.Rest) {
// Middle of target is exactly one rest element. If middle of source is not empty, infer union of middle element types.
Expand Down
19 changes: 10 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5424,15 +5424,16 @@ namespace ts {

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think without the explanation, this is unclear.

HomomorphicMappedType = 1 << 2, // Reverse inference for homomorphic mapped type
PartialHomomorphicMappedType = 1 << 3, // Partial reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 4, // Reverse inference for mapped type
ContravariantConditional = 1 << 5, // Conditional type in contravariant position
ReturnType = 1 << 6, // Inference made from return type of generic function
LiteralKeyof = 1 << 7, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 8, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 9, // Always use strict rules for contravariant inferences
MaxValue = 1 << 10, // Seed for inference priority tracking

PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
Circularity = -1, // Inference circularity (value less than all other priorities)
Expand Down
21 changes: 11 additions & 10 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2645,16 +2645,17 @@ declare namespace ts {
}
export enum InferencePriority {
NakedTypeVariable = 1,
HomomorphicMappedType = 2,
PartialHomomorphicMappedType = 4,
MappedTypeConstraint = 8,
ContravariantConditional = 16,
ReturnType = 32,
LiteralKeyof = 64,
NoConstraints = 128,
AlwaysStrict = 256,
MaxValue = 512,
PriorityImpliesCombination = 104,
SpeculativeTuple = 2,
HomomorphicMappedType = 4,
PartialHomomorphicMappedType = 8,
MappedTypeConstraint = 16,
ContravariantConditional = 32,
ReturnType = 64,
LiteralKeyof = 128,
NoConstraints = 256,
AlwaysStrict = 512,
MaxValue = 1024,
PriorityImpliesCombination = 208,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
Expand Down
21 changes: 11 additions & 10 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2645,16 +2645,17 @@ declare namespace ts {
}
export enum InferencePriority {
NakedTypeVariable = 1,
HomomorphicMappedType = 2,
PartialHomomorphicMappedType = 4,
MappedTypeConstraint = 8,
ContravariantConditional = 16,
ReturnType = 32,
LiteralKeyof = 64,
NoConstraints = 128,
AlwaysStrict = 256,
MaxValue = 512,
PriorityImpliesCombination = 104,
SpeculativeTuple = 2,
HomomorphicMappedType = 4,
PartialHomomorphicMappedType = 8,
MappedTypeConstraint = 16,
ContravariantConditional = 32,
ReturnType = 64,
LiteralKeyof = 128,
NoConstraints = 256,
AlwaysStrict = 512,
MaxValue = 1024,
PriorityImpliesCombination = 208,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */
Expand Down
66 changes: 38 additions & 28 deletions tests/baselines/reference/variadicTuples1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,11 @@ tests/cases/conformance/types/tuple/variadicTuples1.ts(191,5): error TS2322: Typ
'U' could be instantiated with an arbitrary type which could be unrelated to 'readonly string[]'.
tests/cases/conformance/types/tuple/variadicTuples1.ts(203,5): error TS2322: Type 'string' is not assignable to type 'keyof [1, 2, ...T]'.
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"'.
tests/cases/conformance/types/tuple/variadicTuples1.ts(333,14): error TS7019: Rest parameter 'x' implicitly has an 'any[]' type.
tests/cases/conformance/types/tuple/variadicTuples1.ts(341,19): error TS2322: Type 'string' is not assignable to type 'number | undefined'.
tests/cases/conformance/types/tuple/variadicTuples1.ts(342,19): error TS2322: Type 'string' is not assignable to type 'number | undefined'.
tests/cases/conformance/types/tuple/variadicTuples1.ts(346,14): error TS7019: Rest parameter 'x' implicitly has an 'any[]' type.
tests/cases/conformance/types/tuple/variadicTuples1.ts(354,26): error TS2322: Type 'string' is not assignable to type 'number | undefined'.


==== tests/cases/conformance/types/tuple/variadicTuples1.ts (21 errors) ====
==== tests/cases/conformance/types/tuple/variadicTuples1.ts (20 errors) ====
// Variadics in tuple types

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

type First<T extends readonly unknown[]> = T[0];
type DropFirst<T extends readonly unknown[]> = T extends readonly [any, ...infer U] ? U : [...T];
type DropFirst<T extends readonly unknown[]> = T extends readonly [any?, ...infer U] ? U : [...T];

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

type T00 = First<[number, symbol, string]>;
type T01 = First<[symbol, string]>;
type T02 = First<[string]>;
type T03 = First<[number, symbol, ...string[]]>;
type T04 = First<[symbol, ...string[]]>;
type T05 = First<string[]>;
type T06 = First<[]>;
type T07 = First<any>;
type T08 = First<never>;
type T05 = First<[string?]>;
type T06 = First<string[]>;
type T07 = First<[]>;
type T08 = First<any>;
type T09 = First<never>;

type T10 = DropFirst<[number, symbol, string]>;
type T11 = DropFirst<[symbol, string]>;
type T12 = DropFirst<[string]>;
type T13 = DropFirst<[number, symbol, ...string[]]>;
type T14 = DropFirst<[symbol, ...string[]]>;
type T15 = DropFirst<string[]>;
type T16 = DropFirst<[]>;
type T17 = DropFirst<any>;
type T18 = DropFirst<never>;
type T15 = DropFirst<[string?]>;
type T16 = DropFirst<string[]>;
type T17 = DropFirst<[]>;
type T18 = DropFirst<any>;
type T19 = DropFirst<never>;

type T20 = Last<[number, symbol, string]>;
type T21 = Last<[symbol, string]>;
type T22 = Last<[string]>;
type T23 = Last<[number, symbol, ...string[]]>;
type T24 = Last<[symbol, ...string[]]>;
type T25 = Last<string[]>;
type T26 = Last<[]>; // unknown[], maybe should be []
type T27 = Last<any>; // unknown, maybe should be any
type T28 = Last<never>;
type T25 = Last<[string?]>;
type T26 = Last<string[]>;
type T27 = Last<[]>; // unknown, maybe should undefined
type T28 = Last<any>; // unknown, maybe should be any
type T29 = Last<never>;

type T30 = DropLast<[number, symbol, string]>;
type T31 = DropLast<[symbol, string]>;
type T32 = DropLast<[string]>;
type T33 = DropLast<[number, symbol, ...string[]]>;
type T34 = DropLast<[symbol, ...string[]]>;
type T35 = DropLast<string[]>;
type T36 = DropLast<[]>; // unknown[], maybe should be []
type T37 = DropLast<any>;
type T38 = DropLast<never>;
type T35 = DropLast<[string?]>;
type T36 = DropLast<string[]>;
type T37 = DropLast<[]>; // unknown[], maybe should be []
type T38 = DropLast<any>;
type T39 = DropLast<never>;

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

// Inference to [...T] has higher priority than inference to [...T, number?]

declare function ft<T extends unknown[]>(t1: [...T], t2: [...T, number?]): T;

ft([1, 2, 3], [1, 2, 3]);
ft([1, 2], [1, 2, 3]);
ft(['a', 'b'], ['c', 'd'])
ft(['a', 'b'], ['c', 'd', 42])

// Last argument is contextually typed

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

function f21<U extends string[]>(args: [...U, number?]) {
let v1 = f20(args); // U
let v2 = f20(["foo", "bar"]); // []
~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
let v3 = f20(["foo", 42]); // []
~~~~~
let v2 = f20(["foo", "bar"]); // [string]
~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'number | undefined'.
let v3 = f20(["foo", 42]); // [string]
}

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