Skip to content

Commit bb3edc8

Browse files
committed
Control flow for element access expressions
Draft version, just want to see how performance is
1 parent 907664c commit bb3edc8

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/compiler/checker.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20503,7 +20503,45 @@ namespace ts {
2050320503
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
2050420504
AccessFlags.None;
2050520505
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType;
20506-
return checkIndexedAccessIndexType(indexedAccessType, node);
20506+
const prop = indexedAccessType.symbol;
20507+
20508+
// Only compute control flow type if this is a property access expression that isn't an
20509+
// assignment target, and the referenced property was declared as a variable, property,
20510+
// accessor, or optional method.
20511+
// TODO: This probably doesn't handle element access expressions
20512+
const assignmentKind = getAssignmentTargetKind(node);
20513+
if (node.kind !== SyntaxKind.ElementAccessExpression ||
20514+
assignmentKind === AssignmentKind.Definite ||
20515+
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && indexedAccessType.flags & TypeFlags.Union)) {
20516+
return checkIndexedAccessIndexType(indexedAccessType, node);
20517+
}
20518+
// If strict null checks and strict property initialization checks are enabled, if we have
20519+
// a this.xxx property access, if the property is an instance property without an initializer,
20520+
// and if we are in a constructor of the same class as the property declaration, assume that
20521+
// the property is uninitialized at the top of the control flow.
20522+
let assumeUninitialized = false;
20523+
if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) {
20524+
const declaration = prop && prop.valueDeclaration;
20525+
if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
20526+
const flowContainer = getControlFlowContainer(node);
20527+
if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) {
20528+
assumeUninitialized = true;
20529+
}
20530+
}
20531+
}
20532+
else if (strictNullChecks && prop && prop.valueDeclaration &&
20533+
isPropertyAccessExpression(prop.valueDeclaration) &&
20534+
getAssignmentDeclarationPropertyAccessKind(prop.valueDeclaration) &&
20535+
getControlFlowContainer(node) === getControlFlowContainer(prop.valueDeclaration)) {
20536+
assumeUninitialized = true;
20537+
}
20538+
const flowType = getFlowTypeOfReference(node, indexedAccessType, assumeUninitialized ? getOptionalType(indexedAccessType) : indexedAccessType);
20539+
if (assumeUninitialized && !(getFalsyFlags(indexedAccessType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
20540+
error(indexExpression, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop!)); // TODO: GH#18217
20541+
// Return the declared type to reduce follow-on errors
20542+
return indexedAccessType;
20543+
}
20544+
return checkIndexedAccessIndexType(assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType, node);
2050720545
}
2050820546

2050920547
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @strict: true
2+
declare const config: {
3+
[key: string]: boolean | { prop: string };
4+
};
5+
6+
if (typeof config['works'] !== 'boolean') {
7+
config.works.prop = 'test'; // ok
8+
config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string }
9+
}
10+
if (typeof config.works !== 'boolean') {
11+
config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string }
12+
config.works.prop = 'test'; // ok
13+
}

0 commit comments

Comments
 (0)