mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
Fix narrow-by-constructor logic (#37698)
* Fix narrow-by-constructor logic * Add regression test
This commit is contained in:
@@ -20265,12 +20265,6 @@ namespace ts {
|
||||
if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) {
|
||||
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, left, assumeTrue);
|
||||
}
|
||||
if (isConstructorAccessExpression(left)) {
|
||||
return narrowTypeByConstructor(type, left, operator, right, assumeTrue);
|
||||
}
|
||||
if (isConstructorAccessExpression(right)) {
|
||||
return narrowTypeByConstructor(type, right, operator, left, assumeTrue);
|
||||
}
|
||||
if (isMatchingReference(reference, left)) {
|
||||
return narrowTypeByEquality(type, operator, right, assumeTrue);
|
||||
}
|
||||
@@ -20291,6 +20285,12 @@ namespace ts {
|
||||
if (isMatchingReferenceDiscriminant(right, declaredType)) {
|
||||
return narrowTypeByDiscriminant(type, <AccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
|
||||
}
|
||||
if (isMatchingConstructorReference(left)) {
|
||||
return narrowTypeByConstructor(type, operator, right, assumeTrue);
|
||||
}
|
||||
if (isMatchingConstructorReference(right)) {
|
||||
return narrowTypeByConstructor(type, operator, left, assumeTrue);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
return narrowTypeByInstanceof(type, expr, assumeTrue);
|
||||
@@ -20564,17 +20564,18 @@ namespace ts {
|
||||
return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts);
|
||||
}
|
||||
|
||||
function narrowTypeByConstructor(type: Type, constructorAccessExpr: AccessExpression, operator: SyntaxKind, identifier: Expression, assumeTrue: boolean): Type {
|
||||
function isMatchingConstructorReference(expr: Expression) {
|
||||
return (isPropertyAccessExpression(expr) && idText(expr.name) === "constructor" ||
|
||||
isElementAccessExpression(expr) && isStringLiteralLike(expr.argumentExpression) && expr.argumentExpression.text === "constructor") &&
|
||||
isMatchingReference(reference, expr.expression);
|
||||
}
|
||||
|
||||
function narrowTypeByConstructor(type: Type, operator: SyntaxKind, identifier: Expression, assumeTrue: boolean): Type {
|
||||
// Do not narrow when checking inequality.
|
||||
if (assumeTrue ? (operator !== SyntaxKind.EqualsEqualsToken && operator !== SyntaxKind.EqualsEqualsEqualsToken) : (operator !== SyntaxKind.ExclamationEqualsToken && operator !== SyntaxKind.ExclamationEqualsEqualsToken)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// In the case of `x.y`, a `x.constructor === T` type guard resets the narrowed type of `y` to its declared type.
|
||||
if (!isMatchingReference(reference, constructorAccessExpr.expression)) {
|
||||
return declaredType;
|
||||
}
|
||||
|
||||
// Get the type of the constructor identifier expression, if it is not a function then do not narrow.
|
||||
const identifierType = getTypeOfExpression(identifier);
|
||||
if (!isFunctionType(identifierType) && !isConstructorType(identifierType)) {
|
||||
|
||||
@@ -4432,13 +4432,6 @@ namespace ts {
|
||||
return isPropertyAccessExpression(node) && isEntityNameExpression(node.expression);
|
||||
}
|
||||
|
||||
export function isConstructorAccessExpression(expr: Expression): expr is AccessExpression {
|
||||
return (
|
||||
isPropertyAccessExpression(expr) && idText(expr.name) === "constructor" ||
|
||||
isElementAccessExpression(expr) && isStringLiteralLike(expr.argumentExpression) && expr.argumentExpression.text === "constructor"
|
||||
);
|
||||
}
|
||||
|
||||
export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined {
|
||||
if (isPropertyAccessExpression(expr)) {
|
||||
const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression);
|
||||
|
||||
Reference in New Issue
Block a user