@@ -29007,18 +29007,45 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
29007
29007
return undefined;
29008
29008
}
29009
29009
29010
- // In an array literal contextually typed by a type T, the contextual type of an element expression at index N is
29011
- // the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature,
29012
- // it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated
29013
- // type of T.
29014
- function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined {
29015
- return arrayContextualType && (
29016
- index >= 0 && getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) ||
29017
- mapType(arrayContextualType, t =>
29018
- isTupleType(t) ?
29019
- getElementTypeOfSliceOfTupleType(t, 0, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true) :
29020
- getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false),
29021
- /*noReductions*/ true));
29010
+ function getSpreadIndices(elements: readonly Node[]) {
29011
+ let first, last;
29012
+ for (let i = 0; i < elements.length; i++) {
29013
+ if (isSpreadElement(elements[i])) {
29014
+ first ??= i;
29015
+ last = i;
29016
+ }
29017
+ }
29018
+ return { first, last };
29019
+ }
29020
+
29021
+ function getContextualTypeForElementExpression(type: Type | undefined, index: number, length?: number, firstSpreadIndex?: number, lastSpreadIndex?: number): Type | undefined {
29022
+ return type && mapType(type, t => {
29023
+ if (isTupleType(t)) {
29024
+ // If index is before any spread element and within the fixed part of the contextual tuple type, return
29025
+ // the type of the contextual tuple element.
29026
+ if ((firstSpreadIndex === undefined || index < firstSpreadIndex) && index < t.target.fixedLength) {
29027
+ return getTypeArguments(t)[index];
29028
+ }
29029
+ // When the length is known and the index is after all spread elements we compute the offset from the element
29030
+ // to the end and the number of ending fixed elements in the contextual tuple type.
29031
+ const offset = length !== undefined && (lastSpreadIndex === undefined || index > lastSpreadIndex) ? length - index : 0;
29032
+ const fixedEndLength = offset > 0 && t.target.hasRestElement ? getEndElementCount(t.target, ElementFlags.Fixed) : 0;
29033
+ // If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual
29034
+ // tuple element.
29035
+ if (offset > 0 && offset <= fixedEndLength) {
29036
+ return getTypeArguments(t)[getTypeReferenceArity(t) - offset];
29037
+ }
29038
+ // Return a union of the possible contextual element types with no subtype reduction.
29039
+ return getElementTypeOfSliceOfTupleType(t,
29040
+ firstSpreadIndex === undefined ? t.target.fixedLength : Math.min(t.target.fixedLength, firstSpreadIndex),
29041
+ length === undefined || lastSpreadIndex === undefined ? fixedEndLength : Math.min(fixedEndLength, length - lastSpreadIndex),
29042
+ /*writing*/ false, /*noReductions*/ true);
29043
+ }
29044
+ // If element index is known and a contextual property with that name exists, return it. Otherwise return the
29045
+ // iterated or element type of the contextual type.
29046
+ return (!firstSpreadIndex || index < firstSpreadIndex) && getTypeOfPropertyOfContextualType(type, "" + index as __String) ||
29047
+ getIteratedTypeOrElementType(IterationUse.Element, t, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false);
29048
+ }, /*noReductions*/ true);
29022
29049
}
29023
29050
29024
29051
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
@@ -29249,12 +29276,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
29249
29276
case SyntaxKind.ArrayLiteralExpression: {
29250
29277
const arrayLiteral = parent as ArrayLiteralExpression;
29251
29278
const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags);
29252
- // The index of an array literal element doesn't necessarily line up with the index of the corresponding
29253
- // element in a contextual tuple type when there are preceding spread elements in the array literal. For
29254
- // this reason we only pass indices for elements that precede the first spread element.
29255
- const spreadIndex = getNodeLinks(arrayLiteral).firstSpreadIndex ??= findIndex(arrayLiteral.elements, isSpreadElement);
29256
29279
const elementIndex = indexOfNode(arrayLiteral.elements, node);
29257
- return getContextualTypeForElementExpression(type, spreadIndex < 0 || elementIndex < spreadIndex ? elementIndex : -1);
29280
+ const spreadIndices = getNodeLinks(arrayLiteral).spreadIndices ??= getSpreadIndices(arrayLiteral.elements);
29281
+ return getContextualTypeForElementExpression(type, elementIndex, arrayLiteral.elements.length, spreadIndices.first, spreadIndices.last);
29258
29282
}
29259
29283
case SyntaxKind.ConditionalExpression:
29260
29284
return getContextualTypeForConditionalOperand(node, contextFlags);
@@ -32236,7 +32260,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
32236
32260
}
32237
32261
}
32238
32262
else {
32239
- const contextualType = getIndexedAccessType(restType, getNumberLiteralType(i - index), AccessFlags.Contextual);
32263
+ const contextualType = isTupleType(restType) ?
32264
+ getContextualTypeForElementExpression(restType, i - index, argCount - index) || unknownType :
32265
+ getIndexedAccessType(restType, getNumberLiteralType(i - index), AccessFlags.Contextual);
32240
32266
const argType = checkExpressionWithContextualType(arg, contextualType, context, checkMode);
32241
32267
const hasPrimitiveContextualType = inConstContext || maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping);
32242
32268
types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType));
0 commit comments