Skip to content

Commit 3448164

Browse files
committed
Obtain apparent type before narrowing type variables
1 parent 78df754 commit 3448164

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

src/compiler/checker.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5775,11 +5775,11 @@ namespace ts {
57755775
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type;
57765776
return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(<IntersectionType>t) :
57775777
t.flags & TypeFlags.StringLike ? globalStringType :
5778-
t.flags & TypeFlags.NumberLike ? globalNumberType :
5779-
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
5780-
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
5781-
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
5782-
t;
5778+
t.flags & TypeFlags.NumberLike ? globalNumberType :
5779+
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
5780+
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
5781+
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
5782+
t;
57835783
}
57845784

57855785
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
@@ -10439,11 +10439,13 @@ namespace ts {
1043910439
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
1044010440
// separated by dots). The key consists of the id of the symbol referenced by the
1044110441
// leftmost identifier followed by zero or more property names separated by dots.
10442-
// The result is undefined if the reference isn't a dotted name.
10442+
// The result is undefined if the reference isn't a dotted name. We prefix nodes
10443+
// occurring in an apparent type position with '@' because the control flow type
10444+
// of such nodes may be based on the apparent type instead of the declared type.
1044310445
function getFlowCacheKey(node: Node): string {
1044410446
if (node.kind === SyntaxKind.Identifier) {
1044510447
const symbol = getResolvedSymbol(<Identifier>node);
10446-
return symbol !== unknownSymbol ? "" + getSymbolId(symbol) : undefined;
10448+
return symbol !== unknownSymbol ? (isApparentTypePosition(node) ? "@" : "") + getSymbolId(symbol) : undefined;
1044710449
}
1044810450
if (node.kind === SyntaxKind.ThisKeyword) {
1044910451
return "0";
@@ -11708,6 +11710,28 @@ namespace ts {
1170811710
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
1170911711
}
1171011712

11713+
function isApparentTypePosition(node: Node) {
11714+
// When a node is the left hand expression of a property access or call expression, the node occurs
11715+
// in an apparent type position. In such a position we fetch the apparent type of the node *before*
11716+
// performing control flow analysis such that, if the node is a type variable, we apply narrowings
11717+
// to the constraint type.
11718+
const parent = node.parent;
11719+
return parent.kind === SyntaxKind.PropertyAccessExpression ||
11720+
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
11721+
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node;
11722+
}
11723+
11724+
function getDeclaredOrApparentType(symbol: Symbol, node: Node) {
11725+
const type = getTypeOfSymbol(symbol);
11726+
if (isApparentTypePosition(node) && maybeTypeOfKind(type, TypeFlags.TypeVariable)) {
11727+
const apparentType = mapType(getWidenedType(type), getApparentType);
11728+
if (apparentType !== emptyObjectType) {
11729+
return apparentType;
11730+
}
11731+
}
11732+
return type;
11733+
}
11734+
1171111735
function checkIdentifier(node: Identifier): Type {
1171211736
const symbol = getResolvedSymbol(node);
1171311737
if (symbol === unknownSymbol) {
@@ -11783,7 +11807,7 @@ namespace ts {
1178311807
checkCollisionWithCapturedNewTargetVariable(node, node);
1178411808
checkNestedBlockScopedBinding(node, symbol);
1178511809

11786-
const type = getTypeOfSymbol(localOrExportSymbol);
11810+
const type = getDeclaredOrApparentType(localOrExportSymbol, node);
1178711811
const declaration = localOrExportSymbol.valueDeclaration;
1178811812
const assignmentKind = getAssignmentTargetKind(node);
1178911813

@@ -14141,7 +14165,7 @@ namespace ts {
1414114165

1414214166
checkPropertyAccessibility(node, left, apparentType, prop);
1414314167

14144-
const propType = getTypeOfSymbol(prop);
14168+
const propType = getDeclaredOrApparentType(prop, node);
1414514169
const assignmentKind = getAssignmentTargetKind(node);
1414614170

1414714171
if (assignmentKind) {

0 commit comments

Comments
 (0)