mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 21:06:50 -05:00
More correctness
This commit is contained in:
@@ -6275,37 +6275,19 @@ namespace ts {
|
||||
// Get the narrowed type of a given symbol at a given location
|
||||
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
|
||||
let type = getTypeOfSymbol(symbol);
|
||||
let originalType = type;
|
||||
// Only narrow when symbol is variable of type any or an object, union, or type parameter type
|
||||
if (node && symbol.flags & SymbolFlags.Variable) {
|
||||
if (isTypeAny(type) || type.flags & (TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) {
|
||||
let originalType = type;
|
||||
let nodeStack: {node: Node, child: Node}[] = [];
|
||||
loop: while (node.parent) {
|
||||
let child = node;
|
||||
node = node.parent;
|
||||
let narrowedType = type;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.IfStatement:
|
||||
// In a branch of an if statement, narrow based on controlling expression
|
||||
if (child !== (<IfStatement>node).expression) {
|
||||
narrowedType = narrowType(type, (<IfStatement>node).expression, /*assumeTrue*/ child === (<IfStatement>node).thenStatement);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
// In a branch of a conditional expression, narrow based on controlling condition
|
||||
if (child !== (<ConditionalExpression>node).condition) {
|
||||
narrowedType = narrowType(type, (<ConditionalExpression>node).condition, /*assumeTrue*/ child === (<ConditionalExpression>node).whenTrue);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
// In the right operand of an && or ||, narrow based on left operand
|
||||
if (child === (<BinaryExpression>node).right) {
|
||||
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
|
||||
narrowedType = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ true);
|
||||
}
|
||||
else if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken) {
|
||||
narrowedType = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ false);
|
||||
}
|
||||
}
|
||||
nodeStack.push({node, child});
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
@@ -6318,12 +6300,42 @@ namespace ts {
|
||||
// Stop at the first containing function or module declaration
|
||||
break loop;
|
||||
}
|
||||
// Use narrowed type if construct contains no assignments to variable
|
||||
if (narrowedType !== type) {
|
||||
if (isVariableAssignedWithin(symbol, node)) {
|
||||
}
|
||||
|
||||
let nodes: {node: Node, child: Node};
|
||||
while (nodes = nodeStack.pop()) {
|
||||
let {node, child} = nodes;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.IfStatement:
|
||||
// In a branch of an if statement, narrow based on controlling expression
|
||||
if (child !== (<IfStatement>node).expression) {
|
||||
type = narrowType(type, (<IfStatement>node).expression, /*assumeTrue*/ child === (<IfStatement>node).thenStatement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
type = narrowedType;
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
// In a branch of a conditional expression, narrow based on controlling condition
|
||||
if (child !== (<ConditionalExpression>node).condition) {
|
||||
type = narrowType(type, (<ConditionalExpression>node).condition, /*assumeTrue*/ child === (<ConditionalExpression>node).whenTrue);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
// In the right operand of an && or ||, narrow based on left operand
|
||||
if (child === (<BinaryExpression>node).right) {
|
||||
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
|
||||
type = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ true);
|
||||
}
|
||||
else if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken) {
|
||||
type = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.fail("Unreachable!");
|
||||
}
|
||||
|
||||
// Use original type if construct contains assignments to variable
|
||||
if (!nodeStack.length && isVariableAssignedWithin(symbol, node)) {
|
||||
type = originalType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6365,9 +6377,13 @@ namespace ts {
|
||||
// At this point we can bail if it's not a union
|
||||
if (!(type.flags & TypeFlags.Union)) {
|
||||
// If the active non-union type would be removed from a union by this type guard, return an empty union
|
||||
return (assumeTrue === !!(type.flags & flags)) ? type : getUnionType(emptyArray);
|
||||
return filterUnion(type) ? type : getUnionType(emptyArray);
|
||||
}
|
||||
return getUnionType(filter((type as UnionType).types, filterUnion), /*noSubtypeReduction*/ true);
|
||||
|
||||
function filterUnion(t: Type) {
|
||||
return assumeTrue === !!(t.flags & flags);
|
||||
}
|
||||
return getUnionType(filter((type as UnionType).types, t => assumeTrue === !!(t.flags & flags)), /*noSubtypeReduction*/ true);
|
||||
}
|
||||
|
||||
function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
|
||||
|
||||
Reference in New Issue
Block a user