mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 01:00:41 -05:00
Widen widening literal types through compound-like assignments (#52493)
This commit is contained in:
committed by
GitHub
parent
b8b0d26cb9
commit
cac899d44d
@@ -554,6 +554,7 @@ import {
|
||||
isImportOrExportSpecifier,
|
||||
isImportSpecifier,
|
||||
isImportTypeNode,
|
||||
isInCompoundLikeAssignment,
|
||||
isIndexedAccessTypeNode,
|
||||
isInExpressionContext,
|
||||
isInfinityOrNaNString,
|
||||
@@ -26729,10 +26730,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const assignedType = getWidenedLiteralType(getInitialOrAssignedType(flow));
|
||||
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
|
||||
}
|
||||
if (declaredType.flags & TypeFlags.Union) {
|
||||
return getAssignmentReducedType(declaredType as UnionType, getInitialOrAssignedType(flow));
|
||||
const t = isInCompoundLikeAssignment(node) ? getBaseTypeOfLiteralType(declaredType) : declaredType;
|
||||
if (t.flags & TypeFlags.Union) {
|
||||
return getAssignmentReducedType(t as UnionType, getInitialOrAssignedType(flow));
|
||||
}
|
||||
return declaredType;
|
||||
return t;
|
||||
}
|
||||
// We didn't have a direct match. However, if the reference is a dotted name, this
|
||||
// may be an assignment to a left hand part of the reference. For example, for a
|
||||
@@ -28079,7 +28081,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// entities we simply return the declared type.
|
||||
if (localOrExportSymbol.flags & SymbolFlags.Variable) {
|
||||
if (assignmentKind === AssignmentKind.Definite) {
|
||||
return type;
|
||||
return isInCompoundLikeAssignment(node) ? getBaseTypeOfLiteralType(type) : type;
|
||||
}
|
||||
}
|
||||
else if (isAlias) {
|
||||
|
||||
@@ -1222,7 +1222,8 @@ function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator {
|
||||
|| kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken;
|
||||
}
|
||||
|
||||
function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher {
|
||||
/** @internal */
|
||||
export function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher {
|
||||
return isShiftOperator(kind)
|
||||
|| isAdditiveOperatorOrHigher(kind);
|
||||
}
|
||||
|
||||
@@ -152,8 +152,6 @@ import {
|
||||
forEachChild,
|
||||
forEachChildRecursively,
|
||||
ForInOrOfStatement,
|
||||
ForInStatement,
|
||||
ForOfStatement,
|
||||
ForStatement,
|
||||
FunctionBody,
|
||||
FunctionDeclaration,
|
||||
@@ -331,6 +329,7 @@ import {
|
||||
isQualifiedName,
|
||||
isRootedDiskPath,
|
||||
isSetAccessorDeclaration,
|
||||
isShiftOperatorOrHigher,
|
||||
isShorthandPropertyAssignment,
|
||||
isSourceFile,
|
||||
isString,
|
||||
@@ -3418,9 +3417,9 @@ export function isInExpressionContext(node: Node): boolean {
|
||||
forStatement.incrementor === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forInStatement = parent as ForInStatement | ForOfStatement;
|
||||
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forInStatement.expression === node;
|
||||
const forInOrOfStatement = parent as ForInOrOfStatement;
|
||||
return (forInOrOfStatement.initializer === node && forInOrOfStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
|
||||
forInOrOfStatement.expression === node;
|
||||
case SyntaxKind.TypeAssertionExpression:
|
||||
case SyntaxKind.AsExpression:
|
||||
return node === (parent as AssertionExpression).expression;
|
||||
@@ -4468,23 +4467,29 @@ export const enum AssignmentKind {
|
||||
None, Definite, Compound
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getAssignmentTargetKind(node: Node): AssignmentKind {
|
||||
type AssignmentTarget =
|
||||
| BinaryExpression
|
||||
| PrefixUnaryExpression
|
||||
| PostfixUnaryExpression
|
||||
| ForInOrOfStatement;
|
||||
|
||||
function getAssignmentTarget(node: Node): AssignmentTarget | undefined {
|
||||
let parent = node.parent;
|
||||
while (true) {
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
const binaryOperator = (parent as BinaryExpression).operatorToken.kind;
|
||||
return isAssignmentOperator(binaryOperator) && (parent as BinaryExpression).left === node ?
|
||||
binaryOperator === SyntaxKind.EqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) ? AssignmentKind.Definite : AssignmentKind.Compound :
|
||||
AssignmentKind.None;
|
||||
const binaryExpression = parent as BinaryExpression;
|
||||
const binaryOperator = binaryExpression.operatorToken.kind;
|
||||
return isAssignmentOperator(binaryOperator) && binaryExpression.left === node ? binaryExpression : undefined;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
const unaryOperator = (parent as PrefixUnaryExpression | PostfixUnaryExpression).operator;
|
||||
return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? AssignmentKind.Compound : AssignmentKind.None;
|
||||
const unaryExpression = (parent as PrefixUnaryExpression | PostfixUnaryExpression);
|
||||
const unaryOperator = unaryExpression.operator;
|
||||
return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? unaryExpression : undefined;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return (parent as ForInOrOfStatement).initializer === node ? AssignmentKind.Definite : AssignmentKind.None;
|
||||
const forInOrOfStatement = parent as ForInOrOfStatement;
|
||||
return forInOrOfStatement.initializer === node ? forInOrOfStatement : undefined;
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.SpreadElement:
|
||||
@@ -4496,30 +4501,62 @@ export function getAssignmentTargetKind(node: Node): AssignmentKind {
|
||||
break;
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
if ((parent as ShorthandPropertyAssignment).name !== node) {
|
||||
return AssignmentKind.None;
|
||||
return undefined;
|
||||
}
|
||||
node = parent.parent;
|
||||
break;
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
if ((parent as ShorthandPropertyAssignment).name === node) {
|
||||
return AssignmentKind.None;
|
||||
if ((parent as PropertyAssignment).name === node) {
|
||||
return undefined;
|
||||
}
|
||||
node = parent.parent;
|
||||
break;
|
||||
default:
|
||||
return AssignmentKind.None;
|
||||
return undefined;
|
||||
}
|
||||
parent = node.parent;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getAssignmentTargetKind(node: Node): AssignmentKind {
|
||||
const target = getAssignmentTarget(node);
|
||||
if (!target) {
|
||||
return AssignmentKind.None;
|
||||
}
|
||||
switch (target.kind) {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
const binaryOperator = target.operatorToken.kind;
|
||||
return binaryOperator === SyntaxKind.EqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) ?
|
||||
AssignmentKind.Definite :
|
||||
AssignmentKind.Compound;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return AssignmentKind.Compound;
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return AssignmentKind.Definite;
|
||||
}
|
||||
}
|
||||
|
||||
// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
|
||||
// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is
|
||||
// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'.
|
||||
// (Note that `p` is not a target in the above examples, only `a`.)
|
||||
/** @internal */
|
||||
export function isAssignmentTarget(node: Node): boolean {
|
||||
return getAssignmentTargetKind(node) !== AssignmentKind.None;
|
||||
return !!getAssignmentTarget(node);
|
||||
}
|
||||
|
||||
function isCompoundLikeAssignment(assignment: AssignmentExpression<EqualsToken>): boolean {
|
||||
const right = skipParentheses(assignment.right);
|
||||
return right.kind === SyntaxKind.BinaryExpression && isShiftOperatorOrHigher((right as BinaryExpression).operatorToken.kind);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isInCompoundLikeAssignment(node: Node): boolean {
|
||||
const target = getAssignmentTarget(node);
|
||||
return !!target && isAssignmentExpression(target, /*excludeCompoundAssignment*/ true) && isCompoundLikeAssignment(target);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -4534,8 +4571,7 @@ export type NodeWithPossibleHoistedDeclaration =
|
||||
| DefaultClause
|
||||
| LabeledStatement
|
||||
| ForStatement
|
||||
| ForInStatement
|
||||
| ForOfStatement
|
||||
| ForInOrOfStatement
|
||||
| DoStatement
|
||||
| WhileStatement
|
||||
| TryStatement
|
||||
|
||||
Reference in New Issue
Block a user