Merge pull request #19838 from Microsoft/narrow-index-signature-property-access
Narrow property access of undeclared properties from string index signatures
This commit is contained in:
commit
76a3be7c4c
5 changed files with 120 additions and 9 deletions
|
@ -15221,7 +15221,7 @@ namespace ts {
|
|||
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
|
||||
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
|
||||
}
|
||||
return indexInfo.type;
|
||||
return getFlowTypeOfPropertyAccess(node, /*prop*/ undefined, indexInfo.type, getAssignmentTargetKind(node));
|
||||
}
|
||||
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
|
||||
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
|
||||
|
@ -15246,16 +15246,21 @@ namespace ts {
|
|||
return unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 || assignmentKind === AssignmentKind.Definite ||
|
||||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
|
||||
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
|
||||
return propType;
|
||||
return getFlowTypeOfPropertyAccess(node, prop, propType, assignmentKind);
|
||||
}
|
||||
const flowType = getFlowTypeOfReference(node, propType);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function getFlowTypeOfPropertyAccess(node: PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, type: Type, assignmentKind: AssignmentKind) {
|
||||
if (node.kind !== SyntaxKind.PropertyAccessExpression ||
|
||||
assignmentKind === AssignmentKind.Definite ||
|
||||
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && type.flags & TypeFlags.Union)) {
|
||||
return type;
|
||||
}
|
||||
const flowType = getFlowTypeOfReference(node, type);
|
||||
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
|
||||
}
|
||||
|
||||
|
|
20
tests/baselines/reference/controlFlowStringIndex.js
Normal file
20
tests/baselines/reference/controlFlowStringIndex.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
//// [controlFlowStringIndex.ts]
|
||||
type A = {
|
||||
other: number | null;
|
||||
[index: string]: number | null
|
||||
};
|
||||
declare const value: A;
|
||||
if (value.foo !== null) {
|
||||
value.foo.toExponential()
|
||||
value.other // should still be number | null
|
||||
value.bar // should still be number | null
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowStringIndex.js]
|
||||
"use strict";
|
||||
if (value.foo !== null) {
|
||||
value.foo.toExponential();
|
||||
value.other; // should still be number | null
|
||||
value.bar; // should still be number | null
|
||||
}
|
32
tests/baselines/reference/controlFlowStringIndex.symbols
Normal file
32
tests/baselines/reference/controlFlowStringIndex.symbols
Normal file
|
@ -0,0 +1,32 @@
|
|||
=== tests/cases/conformance/controlFlow/controlFlowStringIndex.ts ===
|
||||
type A = {
|
||||
>A : Symbol(A, Decl(controlFlowStringIndex.ts, 0, 0))
|
||||
|
||||
other: number | null;
|
||||
>other : Symbol(other, Decl(controlFlowStringIndex.ts, 0, 10))
|
||||
|
||||
[index: string]: number | null
|
||||
>index : Symbol(index, Decl(controlFlowStringIndex.ts, 2, 5))
|
||||
|
||||
};
|
||||
declare const value: A;
|
||||
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
|
||||
>A : Symbol(A, Decl(controlFlowStringIndex.ts, 0, 0))
|
||||
|
||||
if (value.foo !== null) {
|
||||
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
|
||||
|
||||
value.foo.toExponential()
|
||||
>value.foo.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --))
|
||||
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
|
||||
>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --))
|
||||
|
||||
value.other // should still be number | null
|
||||
>value.other : Symbol(other, Decl(controlFlowStringIndex.ts, 0, 10))
|
||||
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
|
||||
>other : Symbol(other, Decl(controlFlowStringIndex.ts, 0, 10))
|
||||
|
||||
value.bar // should still be number | null
|
||||
>value : Symbol(value, Decl(controlFlowStringIndex.ts, 4, 13))
|
||||
}
|
||||
|
43
tests/baselines/reference/controlFlowStringIndex.types
Normal file
43
tests/baselines/reference/controlFlowStringIndex.types
Normal file
|
@ -0,0 +1,43 @@
|
|||
=== tests/cases/conformance/controlFlow/controlFlowStringIndex.ts ===
|
||||
type A = {
|
||||
>A : A
|
||||
|
||||
other: number | null;
|
||||
>other : number | null
|
||||
>null : null
|
||||
|
||||
[index: string]: number | null
|
||||
>index : string
|
||||
>null : null
|
||||
|
||||
};
|
||||
declare const value: A;
|
||||
>value : A
|
||||
>A : A
|
||||
|
||||
if (value.foo !== null) {
|
||||
>value.foo !== null : boolean
|
||||
>value.foo : number | null
|
||||
>value : A
|
||||
>foo : number | null
|
||||
>null : null
|
||||
|
||||
value.foo.toExponential()
|
||||
>value.foo.toExponential() : string
|
||||
>value.foo.toExponential : (fractionDigits?: number | undefined) => string
|
||||
>value.foo : number
|
||||
>value : A
|
||||
>foo : number
|
||||
>toExponential : (fractionDigits?: number | undefined) => string
|
||||
|
||||
value.other // should still be number | null
|
||||
>value.other : number | null
|
||||
>value : A
|
||||
>other : number | null
|
||||
|
||||
value.bar // should still be number | null
|
||||
>value.bar : number | null
|
||||
>value : A
|
||||
>bar : number | null
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// @strict: true
|
||||
type A = {
|
||||
other: number | null;
|
||||
[index: string]: number | null
|
||||
};
|
||||
declare const value: A;
|
||||
if (value.foo !== null) {
|
||||
value.foo.toExponential()
|
||||
value.other // should still be number | null
|
||||
value.bar // should still be number | null
|
||||
}
|
Loading…
Reference in a new issue