mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-24 11:43:18 -05:00
Narrowing from truthy unknown to object (#37507)
* For x && typeof x === 'object', narrow x to just type object * Add tests * Allow arbitrary nesting / add comments * Add additional tests
This commit is contained in:
@@ -19013,6 +19013,13 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Given a source x, check if target matches x or is an && operation with an operand that matches x.
|
||||
function containsTruthyCheck(source: Node, target: Node): boolean {
|
||||
return isMatchingReference(source, target) ||
|
||||
(target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken &&
|
||||
(containsTruthyCheck(source, (<BinaryExpression>target).left) || containsTruthyCheck(source, (<BinaryExpression>target).right)));
|
||||
}
|
||||
|
||||
function getAccessedPropertyName(access: AccessExpression): __String | undefined {
|
||||
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
|
||||
isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
|
||||
@@ -20409,15 +20416,23 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.Any && literal.text === "function") {
|
||||
return type;
|
||||
}
|
||||
if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") {
|
||||
// The pattern x && typeof x === 'object', where x is of type unknown, narrows x to type object. We don't
|
||||
// need to check for the reverse typeof x === 'object' && x since that already narrows correctly.
|
||||
if (typeOfExpr.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const expr = <BinaryExpression>typeOfExpr.parent.parent;
|
||||
if (expr.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken && expr.right === typeOfExpr.parent && containsTruthyCheck(reference, expr.left)) {
|
||||
return nonPrimitiveType;
|
||||
}
|
||||
}
|
||||
return getUnionType([nonPrimitiveType, nullType]);
|
||||
}
|
||||
const facts = assumeTrue ?
|
||||
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
|
||||
typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
|
||||
return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts);
|
||||
|
||||
function narrowTypeForTypeof(type: Type) {
|
||||
if (type.flags & TypeFlags.Unknown && literal.text === "object") {
|
||||
return getUnionType([nonPrimitiveType, nullType]);
|
||||
}
|
||||
// We narrow a non-union type to an exact primitive type if the non-union type
|
||||
// is a supertype of that primitive type. For example, type 'any' can be narrowed
|
||||
// to one of the primitive types.
|
||||
|
||||
Reference in New Issue
Block a user