From 8efff3fcf6d4307fa24bb23a3e5f91cfc1e73a4f Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 30 Aug 2022 18:47:37 +0000 Subject: [PATCH] Avoid freshening literal types when unnecessary. --- src/compiler/checker.ts | 60 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e911d7fe7d2..80c921cfea4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15379,7 +15379,7 @@ namespace ts { return neverType; } return isIdentifier(name) ? getStringLiteralType(unescapeLeadingUnderscores(name.escapedText)) : - getRegularTypeOfLiteralType(isComputedPropertyName(name) ? checkComputedPropertyName(name) : checkExpression(name)); + isComputedPropertyName(name) ? checkComputedPropertyName(name) : checkExpression(name, /*checkMode*/ undefined, CheckExpressionIntent.NoLiteralFreshening); } function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags, includeNonPublic?: boolean) { @@ -18054,7 +18054,7 @@ namespace ts { const oldContext = node.contextualType; node.contextualType = target; try { - const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true); + const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, CheckExpressionIntent.ForceTuple); node.contextualType = oldContext; if (isTupleLikeType(tupleizedType)) { return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation, containingMessageChain, errorOutputContainer); @@ -27869,7 +27869,7 @@ namespace ts { (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken); } - function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type { + function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, checkExpressionIntent: CheckExpressionIntent): Type { const elements = node.elements; const elementCount = elements.length; const elementTypes: Type[] = []; @@ -27884,7 +27884,7 @@ namespace ts { if (languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(e, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray); } - const spreadType = checkExpression((e as SpreadElement).expression, checkMode, forceTuple); + const spreadType = checkExpression((e as SpreadElement).expression, checkMode, checkExpressionIntent); if (isArrayLikeType(spreadType)) { elementTypes.push(spreadType); elementFlags.push(ElementFlags.Variadic); @@ -27920,7 +27920,7 @@ namespace ts { } else { const elementContextualType = getContextualTypeForElementExpression(contextualType, elementTypes.length); - const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple); + const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, checkExpressionIntent); elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression)); elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required); if (contextualType && someType(contextualType, isTupleLikeType) && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) { @@ -27933,7 +27933,7 @@ namespace ts { if (inDestructuringPattern) { return createTupleType(elementTypes, elementFlags); } - if (forceTuple || inConstContext || contextualType && someType(contextualType, isTupleLikeType)) { + if ((checkExpressionIntent & CheckExpressionIntent.ForceTuple) || inConstContext || contextualType && someType(contextualType, isTupleLikeType)) { return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext)); } return createArrayLiteralType(createArrayType(elementTypes.length ? @@ -27981,7 +27981,7 @@ namespace ts { && node.parent.kind !== SyntaxKind.GetAccessor && node.parent.kind !== SyntaxKind.SetAccessor) { return links.resolvedType = errorType; } - links.resolvedType = checkExpression(node.expression); + links.resolvedType = checkExpression(node.expression, /*checMode*/ undefined, CheckExpressionIntent.NoLiteralFreshening); // The computed property name of a non-static class field within a loop must be stored in a block-scoped binding. // (It needs to be bound at class evaluation time.) if (isPropertyDeclaration(node.parent) && !hasStaticModifier(node.parent) && isClassExpression(node.parent.parent)) { @@ -33574,23 +33574,24 @@ namespace ts { return awaitedType; } - function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { + function checkPrefixUnaryExpression(node: PrefixUnaryExpression, checkExpressionIntent: CheckExpressionIntent): Type { const operandType = checkExpression(node.operand); if (operandType === silentNeverType) { return silentNeverType; } + const freshenIfNeeded = checkExpressionIntent & CheckExpressionIntent.NoLiteralFreshening ? identity : getFreshTypeOfLiteralType; switch (node.operand.kind) { case SyntaxKind.NumericLiteral: switch (node.operator) { case SyntaxKind.MinusToken: - return getFreshTypeOfLiteralType(getNumberLiteralType(-(node.operand as NumericLiteral).text)); + return freshenIfNeeded(getNumberLiteralType(-(node.operand as NumericLiteral).text)); case SyntaxKind.PlusToken: - return getFreshTypeOfLiteralType(getNumberLiteralType(+(node.operand as NumericLiteral).text)); + return freshenIfNeeded(getNumberLiteralType(+(node.operand as NumericLiteral).text)); } break; case SyntaxKind.BigIntLiteral: if (node.operator === SyntaxKind.MinusToken) { - return getFreshTypeOfLiteralType(getBigIntLiteralType({ + return freshenIfNeeded(getBigIntLiteralType({ negative: true, base10Value: parsePseudoBigInt((node.operand as BigIntLiteral).text) })); @@ -34865,8 +34866,8 @@ namespace ts { (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } - function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type { - const type = checkExpression(node, checkMode, forceTuple); + function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, checkExpressionIntent?: CheckExpressionIntent): Type { + const type = checkExpression(node, checkMode, checkExpressionIntent); return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) : isTypeAssertion(node) ? type : getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(arguments.length === 2 ? getContextualType(node, /*contextFlags*/ undefined) : contextualType, node, /*contextFlags*/ undefined)); @@ -35126,12 +35127,12 @@ namespace ts { } } - function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { + function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, checkIntent?: CheckExpressionIntent): Type { tracing?.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath }); const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; - const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple); + const uninstantiatedType = checkExpressionWorker(node, checkMode, checkIntent); const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); if (isConstEnumObjectType(type)) { checkConstEnumAccess(node, type); @@ -35174,7 +35175,13 @@ namespace ts { return checkExpression(node.expression, checkMode); } - function checkExpressionWorker(node: Expression | QualifiedName, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { + const enum CheckExpressionIntent { + None = 0, + ForceTuple = 1 << 0, + NoLiteralFreshening = 1 << 1, + } + + function checkExpressionWorker(node: Expression | QualifiedName, checkMode: CheckMode | undefined, intent: CheckExpressionIntent = CheckExpressionIntent.None): Type { const kind = node.kind; if (cancellationToken) { // Only bother checking on a few construct kinds. We don't want to be excessively @@ -35199,16 +35206,25 @@ namespace ts { return nullWideningType; case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getStringLiteralType((node as StringLiteralLike).text)); + const stringLiteralType: Type = getStringLiteralType((node as StringLiteralLike).text); + return intent & CheckExpressionIntent.NoLiteralFreshening ? + stringLiteralType : + getFreshTypeOfLiteralType(stringLiteralType); case SyntaxKind.NumericLiteral: checkGrammarNumericLiteral(node as NumericLiteral); - return getFreshTypeOfLiteralType(getNumberLiteralType(+(node as NumericLiteral).text)); + const numberLiteralType: Type = getNumberLiteralType(+(node as NumericLiteral).text); + return intent & CheckExpressionIntent.NoLiteralFreshening ? + numberLiteralType : + getFreshTypeOfLiteralType(numberLiteralType); case SyntaxKind.BigIntLiteral: checkGrammarBigIntLiteral(node as BigIntLiteral); - return getFreshTypeOfLiteralType(getBigIntLiteralType({ + const bigIntLiteralType: Type = getBigIntLiteralType({ negative: false, base10Value: parsePseudoBigInt((node as BigIntLiteral).text) - })); + }); + return intent & CheckExpressionIntent.NoLiteralFreshening ? + bigIntLiteralType : + getFreshTypeOfLiteralType(bigIntLiteralType); case SyntaxKind.TrueKeyword: return trueType; case SyntaxKind.FalseKeyword: @@ -35218,7 +35234,7 @@ namespace ts { case SyntaxKind.RegularExpressionLiteral: return globalRegExpType; case SyntaxKind.ArrayLiteralExpression: - return checkArrayLiteral(node as ArrayLiteralExpression, checkMode, forceTuple); + return checkArrayLiteral(node as ArrayLiteralExpression, checkMode, intent & CheckExpressionIntent.ForceTuple); case SyntaxKind.ObjectLiteralExpression: return checkObjectLiteral(node as ObjectLiteralExpression, checkMode); case SyntaxKind.PropertyAccessExpression: @@ -35263,7 +35279,7 @@ namespace ts { case SyntaxKind.AwaitExpression: return checkAwaitExpression(node as AwaitExpression); case SyntaxKind.PrefixUnaryExpression: - return checkPrefixUnaryExpression(node as PrefixUnaryExpression); + return checkPrefixUnaryExpression(node as PrefixUnaryExpression, intent); case SyntaxKind.PostfixUnaryExpression: return checkPostfixUnaryExpression(node as PostfixUnaryExpression); case SyntaxKind.BinaryExpression: