Skip to content

Commit 31d335f

Browse files
committed
Improve handling of corner cases in narrowTypeByInstanceof
1 parent e7dfe82 commit 31d335f

File tree

1 file changed

+19
-31
lines changed

1 file changed

+19
-31
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27033,42 +27033,30 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2703327033
}
2703427034
return type;
2703527035
}
27036-
27037-
// Check that right operand is a function type with a prototype property
27038-
const rightType = getTypeOfExpression(expr.right);
27039-
if (!isTypeDerivedFrom(rightType, globalFunctionType)) {
27036+
const instanceType = mapType(getTypeOfExpression(expr.right), getInstanceType);
27037+
// Don't narrow from `any` if the target type is exactly `Object` or `Function`, and narrow
27038+
// in the false branch only if the target is a non-empty object type.
27039+
if (isTypeAny(type) && (instanceType === globalObjectType || instanceType === globalFunctionType) ||
27040+
!assumeTrue && !(instanceType.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(instanceType))) {
2704027041
return type;
2704127042
}
27043+
return getNarrowedType(type, instanceType, assumeTrue, /*checkDerived*/ true);
27044+
}
2704227045

27043-
let targetType: Type | undefined;
27044-
const prototypeProperty = getPropertyOfType(rightType, "prototype" as __String);
27045-
if (prototypeProperty) {
27046-
// Target type is type of the prototype property
27047-
const prototypePropertyType = getTypeOfSymbol(prototypeProperty);
27048-
if (!isTypeAny(prototypePropertyType)) {
27049-
targetType = prototypePropertyType;
27046+
function getInstanceType(constructorType: Type) {
27047+
if (isTypeDerivedFrom(constructorType, globalFunctionType)) {
27048+
const prototypePropertyType = getTypeOfPropertyOfType(constructorType, "prototype" as __String);
27049+
if (prototypePropertyType && !isTypeAny(prototypePropertyType)) {
27050+
return prototypePropertyType;
27051+
}
27052+
const constructSignatures = getSignaturesOfType(constructorType, SignatureKind.Construct);
27053+
if (constructSignatures.length) {
27054+
return getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))));
2705027055
}
2705127056
}
27052-
27053-
// Don't narrow from 'any' if the target type is exactly 'Object' or 'Function'
27054-
if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) {
27055-
return type;
27056-
}
27057-
27058-
if (!targetType) {
27059-
const constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct);
27060-
targetType = constructSignatures.length ?
27061-
getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature)))) :
27062-
emptyObjectType;
27063-
}
27064-
27065-
// We can't narrow a union based off instanceof without negated types see #31576 for more info
27066-
if (!assumeTrue && rightType.flags & TypeFlags.Union) {
27067-
const nonConstructorTypeInUnion = find((rightType as UnionType).types, (t) => !isConstructorType(t));
27068-
if (!nonConstructorTypeInUnion) return type;
27069-
}
27070-
27071-
return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
27057+
// We use the empty object type to indicate we don't know the type of objects created by
27058+
// this constructor function.
27059+
return emptyObjectType;
2707227060
}
2707327061

2707427062
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {

0 commit comments

Comments
 (0)