Skip to content

Commit 7e002ae

Browse files
author
Andy
authored
Avoid calling indexOf when checking array element types (#18619)
* Avoid calling `indexOf` when checking array element types * Add 'indexOfNode' and use it in cases which may handle long lists. (#18635) * Fix bug where contextual type was not reused if undefined
1 parent 1a383ec commit 7e002ae

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13198,16 +13198,11 @@ namespace ts {
1319813198
// the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature,
1319913199
// it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated
1320013200
// type of T.
13201-
function getContextualTypeForElementExpression(node: Expression): Type {
13202-
const arrayLiteral = <ArrayLiteralExpression>node.parent;
13203-
const type = getApparentTypeOfContextualType(arrayLiteral);
13204-
if (type) {
13205-
const index = indexOf(arrayLiteral.elements, node);
13206-
return getTypeOfPropertyOfContextualType(type, "" + index as __String)
13207-
|| getIndexTypeOfContextualType(type, IndexKind.Number)
13208-
|| getIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false);
13209-
}
13210-
return undefined;
13201+
function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined {
13202+
return arrayContextualType && (
13203+
getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String)
13204+
|| getIndexTypeOfContextualType(arrayContextualType, IndexKind.Number)
13205+
|| getIteratedTypeOrElementType(arrayContextualType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false));
1321113206
}
1321213207

1321313208
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
@@ -13321,8 +13316,11 @@ namespace ts {
1332113316
return getContextualTypeForObjectLiteralElement(<ObjectLiteralElementLike>parent);
1332213317
case SyntaxKind.SpreadAssignment:
1332313318
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression);
13324-
case SyntaxKind.ArrayLiteralExpression:
13325-
return getContextualTypeForElementExpression(node);
13319+
case SyntaxKind.ArrayLiteralExpression: {
13320+
const arrayLiteral = <ArrayLiteralExpression>parent;
13321+
const type = getApparentTypeOfContextualType(arrayLiteral);
13322+
return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node));
13323+
}
1332613324
case SyntaxKind.ConditionalExpression:
1332713325
return getContextualTypeForConditionalOperand(node);
1332813326
case SyntaxKind.TemplateSpan:
@@ -13451,12 +13449,14 @@ namespace ts {
1345113449
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
1345213450
}
1345313451

13454-
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode?: CheckMode): Type {
13452+
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined): Type {
1345513453
const elements = node.elements;
1345613454
let hasSpreadElement = false;
1345713455
const elementTypes: Type[] = [];
1345813456
const inDestructuringPattern = isAssignmentTarget(node);
13459-
for (const e of elements) {
13457+
const contextualType = getApparentTypeOfContextualType(node);
13458+
for (let index = 0; index < elements.length; index++) {
13459+
const e = elements[index];
1346013460
if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElement) {
1346113461
// Given the following situation:
1346213462
// var c: {};
@@ -13478,7 +13478,8 @@ namespace ts {
1347813478
}
1347913479
}
1348013480
else {
13481-
const type = checkExpressionForMutableLocation(e, checkMode);
13481+
const elementContextualType = getContextualTypeForElementExpression(contextualType, index);
13482+
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType);
1348213483
elementTypes.push(type);
1348313484
}
1348413485
hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElement;
@@ -18023,9 +18024,13 @@ namespace ts {
1802318024
return false;
1802418025
}
1802518026

18026-
function checkExpressionForMutableLocation(node: Expression, checkMode?: CheckMode): Type {
18027+
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode, contextualType?: Type): Type {
18028+
if (arguments.length === 2) {
18029+
contextualType = getContextualType(node);
18030+
}
1802718031
const type = checkExpression(node, checkMode);
18028-
return isTypeAssertion(node) || isLiteralContextualType(getContextualType(node)) ? type : getWidenedLiteralType(type);
18032+
const shouldWiden = isTypeAssertion(node) || isLiteralContextualType(contextualType);
18033+
return shouldWiden ? type : getWidenedLiteralType(type);
1802918034
}
1803018035

1803118036
function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type {

src/compiler/utilities.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,18 @@ namespace ts {
321321
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
322322
}
323323

324+
/**
325+
* Note: it is expected that the `nodeArray` and the `node` are within the same file.
326+
* For example, searching for a `SourceFile` in a `SourceFile[]` wouldn't work.
327+
*/
328+
export function indexOfNode(nodeArray: ReadonlyArray<Node>, node: Node) {
329+
return binarySearch(nodeArray, node, compareNodePos);
330+
}
331+
332+
function compareNodePos({ pos: aPos }: Node, { pos: bPos}: Node) {
333+
return aPos < bPos ? Comparison.LessThan : bPos < aPos ? Comparison.GreaterThan : Comparison.EqualTo;
334+
}
335+
324336
/**
325337
* Gets flags that control emit behavior of a node.
326338
*/

src/services/textChanges.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ namespace ts.textChanges {
226226
Debug.fail("node is not a list element");
227227
return this;
228228
}
229-
const index = containingList.indexOf(node);
229+
const index = indexOfNode(containingList, node);
230230
if (index < 0) {
231231
return this;
232232
}
@@ -358,7 +358,7 @@ namespace ts.textChanges {
358358
Debug.fail("node is not a list element");
359359
return this;
360360
}
361-
const index = containingList.indexOf(after);
361+
const index = indexOfNode(containingList, after);
362362
if (index < 0) {
363363
return this;
364364
}

src/services/utilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ namespace ts {
597597
}
598598

599599
const children = list.getChildren();
600-
const listItemIndex = indexOf(children, node);
600+
const listItemIndex = indexOfNode(children, node);
601601

602602
return {
603603
listItemIndex,

0 commit comments

Comments
 (0)