mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #8962 from Microsoft/propertyAccessNarrowing
Fix narrowing of property access expressions
This commit is contained in:
+5
-15
@@ -9991,24 +9991,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
const propType = getTypeOfSymbol(prop);
|
||||
// Only compute control flow type if this is a property access expression that isn't an
|
||||
// assignment target, and the referenced property was declared as a variable, property,
|
||||
// accessor, or optional method.
|
||||
if (node.kind !== SyntaxKind.PropertyAccessExpression || isAssignmentTarget(node) ||
|
||||
!(propType.flags & TypeFlags.Union) && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor))) {
|
||||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
|
||||
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
|
||||
return propType;
|
||||
}
|
||||
const leftmostNode = getLeftmostIdentifierOrThis(node);
|
||||
if (!leftmostNode) {
|
||||
return propType;
|
||||
}
|
||||
if (leftmostNode.kind === SyntaxKind.Identifier) {
|
||||
const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(<Identifier>leftmostNode));
|
||||
if (!leftmostSymbol) {
|
||||
return propType;
|
||||
}
|
||||
const declaration = leftmostSymbol.valueDeclaration;
|
||||
if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) {
|
||||
return propType;
|
||||
}
|
||||
}
|
||||
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//// [classStaticPropertyTypeGuard.ts]
|
||||
|
||||
// Repro from #8923
|
||||
|
||||
class A {
|
||||
private static _a: string | undefined;
|
||||
|
||||
public get a(): string {
|
||||
if (A._a) {
|
||||
return A._a; // is possibly null or undefined.
|
||||
}
|
||||
return A._a = 'helloworld';
|
||||
}
|
||||
}
|
||||
|
||||
//// [classStaticPropertyTypeGuard.js]
|
||||
// Repro from #8923
|
||||
var A = (function () {
|
||||
function A() {
|
||||
}
|
||||
Object.defineProperty(A.prototype, "a", {
|
||||
get: function () {
|
||||
if (A._a) {
|
||||
return A._a; // is possibly null or undefined.
|
||||
}
|
||||
return A._a = 'helloworld';
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
return A;
|
||||
}());
|
||||
@@ -0,0 +1,29 @@
|
||||
=== tests/cases/compiler/classStaticPropertyTypeGuard.ts ===
|
||||
|
||||
// Repro from #8923
|
||||
|
||||
class A {
|
||||
>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
|
||||
|
||||
private static _a: string | undefined;
|
||||
>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
|
||||
public get a(): string {
|
||||
>a : Symbol(A.a, Decl(classStaticPropertyTypeGuard.ts, 4, 42))
|
||||
|
||||
if (A._a) {
|
||||
>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
|
||||
>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
|
||||
return A._a; // is possibly null or undefined.
|
||||
>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
|
||||
>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
}
|
||||
return A._a = 'helloworld';
|
||||
>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
|
||||
>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
=== tests/cases/compiler/classStaticPropertyTypeGuard.ts ===
|
||||
|
||||
// Repro from #8923
|
||||
|
||||
class A {
|
||||
>A : A
|
||||
|
||||
private static _a: string | undefined;
|
||||
>_a : string | undefined
|
||||
|
||||
public get a(): string {
|
||||
>a : string
|
||||
|
||||
if (A._a) {
|
||||
>A._a : string | undefined
|
||||
>A : typeof A
|
||||
>_a : string | undefined
|
||||
|
||||
return A._a; // is possibly null or undefined.
|
||||
>A._a : string
|
||||
>A : typeof A
|
||||
>_a : string
|
||||
}
|
||||
return A._a = 'helloworld';
|
||||
>A._a = 'helloworld' : string
|
||||
>A._a : string | undefined
|
||||
>A : typeof A
|
||||
>_a : string | undefined
|
||||
>'helloworld' : string
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// @strictNullChecks: true
|
||||
// @target: ES5
|
||||
|
||||
// Repro from #8923
|
||||
|
||||
class A {
|
||||
private static _a: string | undefined;
|
||||
|
||||
public get a(): string {
|
||||
if (A._a) {
|
||||
return A._a; // is possibly null or undefined.
|
||||
}
|
||||
return A._a = 'helloworld';
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,26 @@ goTo.marker('6');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
|
||||
|
||||
['7', '8', '9'].forEach((marker, index, arr) => {
|
||||
goTo.marker(marker);
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
|
||||
});
|
||||
goTo.marker('7');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
|
||||
|
||||
['7', '8', '9'].forEach((marker, index, arr) => {
|
||||
goTo.marker(marker);
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
|
||||
});
|
||||
goTo.marker('8');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: number');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
|
||||
|
||||
goTo.marker('9');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
|
||||
|
||||
goTo.marker('7');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
|
||||
|
||||
goTo.marker('8');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: number');
|
||||
verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
|
||||
|
||||
goTo.marker('9');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string');
|
||||
verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
|
||||
Reference in New Issue
Block a user