@@ -69,6 +69,7 @@ namespace ts {
69
69
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
70
70
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
71
71
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
72
+ const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
72
73
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
73
74
const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
74
75
@@ -12283,7 +12284,7 @@ namespace ts {
12283
12284
// on empty arrays are possible without implicit any errors and new element types can be inferred without
12284
12285
// type mismatch errors.
12285
12286
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
12286
- if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
12287
+ if (reference.parent && reference.parent .kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
12287
12288
return declaredType;
12288
12289
}
12289
12290
return resultType;
@@ -15561,63 +15562,68 @@ namespace ts {
15561
15562
}
15562
15563
15563
15564
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
15564
- const type = checkNonNullExpression(left);
15565
- if (isTypeAny(type) || type === silentNeverType) {
15566
- return type;
15567
- }
15568
-
15569
- const apparentType = getApparentType(getWidenedType(type));
15570
- if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
15571
- // handle cases when type is Type parameter with invalid or any constraint
15565
+ let propType: Type;
15566
+ const leftType = checkNonNullExpression(left);
15567
+ const apparentType = getApparentType(getWidenedType(leftType));
15568
+ if (isTypeAny(apparentType) || apparentType === silentNeverType) {
15572
15569
return apparentType;
15573
15570
}
15571
+ const assignmentKind = getAssignmentTargetKind(node);
15574
15572
const prop = getPropertyOfType(apparentType, right.escapedText);
15575
15573
if (!prop) {
15576
15574
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
15577
- if (indexInfo && indexInfo.type) {
15578
- if (indexInfo.isReadonly && (isAssignmentTarget( node) || isDeleteTarget(node) )) {
15579
- error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType) );
15575
+ if (!( indexInfo && indexInfo.type) ) {
15576
+ if (right.escapedText && !checkAndReportErrorForExtendingInterface( node)) {
15577
+ reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType );
15580
15578
}
15581
- return getFlowTypeOfPropertyAccess(node, /*prop*/ undefined, indexInfo.type, getAssignmentTargetKind(node)) ;
15579
+ return unknownType ;
15582
15580
}
15583
- if (right.escapedText && !checkAndReportErrorForExtendingInterface( node)) {
15584
- reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type );
15581
+ if (indexInfo.isReadonly && (isAssignmentTarget( node) || isDeleteTarget(node) )) {
15582
+ error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString( apparentType) );
15585
15583
}
15586
- return unknownType ;
15584
+ propType = indexInfo.type ;
15587
15585
}
15588
-
15589
- checkPropertyNotUsedBeforeDeclaration(prop, node, right);
15590
-
15591
- markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
15592
-
15593
- getNodeLinks(node).resolvedSymbol = prop;
15594
-
15595
- checkPropertyAccessibility(node, left, apparentType, prop);
15596
-
15597
- const propType = getDeclaredOrApparentType(prop, node);
15598
- const assignmentKind = getAssignmentTargetKind(node);
15599
-
15600
- if (assignmentKind) {
15601
- if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
15602
- error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
15603
- return unknownType;
15586
+ else {
15587
+ checkPropertyNotUsedBeforeDeclaration(prop, node, right);
15588
+ markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
15589
+ getNodeLinks(node).resolvedSymbol = prop;
15590
+ checkPropertyAccessibility(node, left, apparentType, prop);
15591
+ if (assignmentKind) {
15592
+ if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
15593
+ error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
15594
+ return unknownType;
15595
+ }
15604
15596
}
15597
+ propType = getDeclaredOrApparentType(prop, node);
15605
15598
}
15606
- return getFlowTypeOfPropertyAccess(node, prop, propType, assignmentKind);
15607
- }
15608
-
15609
- /**
15610
- * Only compute control flow type if this is a property access expression that isn't an
15611
- * assignment target, and the referenced property was declared as a variable, property,
15612
- * accessor, or optional method.
15613
- */
15614
- function getFlowTypeOfPropertyAccess(node: PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, type: Type, assignmentKind: AssignmentKind) {
15599
+ // Only compute control flow type if this is a property access expression that isn't an
15600
+ // assignment target, and the referenced property was declared as a variable, property,
15601
+ // accessor, or optional method.
15615
15602
if (node.kind !== SyntaxKind.PropertyAccessExpression ||
15616
15603
assignmentKind === AssignmentKind.Definite ||
15617
- prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && type.flags & TypeFlags.Union)) {
15618
- return type;
15604
+ prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
15605
+ return propType;
15606
+ }
15607
+ // If strict null checks and strict property initialization checks are enabled, if we have
15608
+ // a this.xxx property access, if the property is an instance property without an initializer,
15609
+ // and if we are in a constructor of the same class as the property declaration, assume that
15610
+ // the property is uninitialized at the top of the control flow.
15611
+ let assumeUninitialized = false;
15612
+ if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) {
15613
+ const declaration = prop && prop.valueDeclaration;
15614
+ if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
15615
+ const flowContainer = getControlFlowContainer(node);
15616
+ if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) {
15617
+ assumeUninitialized = true;
15618
+ }
15619
+ }
15620
+ }
15621
+ const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
15622
+ if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
15623
+ error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop));
15624
+ // Return the declared type to reduce follow-on errors
15625
+ return propType;
15619
15626
}
15620
- const flowType = getFlowTypeOfReference(node, type);
15621
15627
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
15622
15628
}
15623
15629
@@ -22483,6 +22489,7 @@ namespace ts {
22483
22489
if (produceDiagnostics) {
22484
22490
checkIndexConstraints(type);
22485
22491
checkTypeForDuplicateIndexSignatures(node);
22492
+ checkPropertyInitialization(node);
22486
22493
}
22487
22494
}
22488
22495
@@ -22639,6 +22646,39 @@ namespace ts {
22639
22646
return ok;
22640
22647
}
22641
22648
22649
+ function checkPropertyInitialization(node: ClassLikeDeclaration) {
22650
+ if (!strictNullChecks || !strictPropertyInitialization || node.flags & NodeFlags.Ambient) {
22651
+ return;
22652
+ }
22653
+ const constructor = findConstructorDeclaration(node);
22654
+ for (const member of node.members) {
22655
+ if (isInstancePropertyWithoutInitializer(member)) {
22656
+ const propName = (<PropertyDeclaration>member).name;
22657
+ if (isIdentifier(propName)) {
22658
+ const type = getTypeOfSymbol(getSymbolOfNode(member));
22659
+ if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) {
22660
+ if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
22661
+ error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName));
22662
+ }
22663
+ }
22664
+ }
22665
+ }
22666
+ }
22667
+ }
22668
+
22669
+ function isInstancePropertyWithoutInitializer(node: Node) {
22670
+ return node.kind === SyntaxKind.PropertyDeclaration &&
22671
+ !hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) &&
22672
+ !(<PropertyDeclaration>node).initializer;
22673
+ }
22674
+
22675
+ function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
22676
+ const reference = createPropertyAccess(createThis(), propName);
22677
+ reference.flowNode = constructor.returnFlowNode;
22678
+ const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
22679
+ return !(getFalsyFlags(flowType) & TypeFlags.Undefined);
22680
+ }
22681
+
22642
22682
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
22643
22683
// Grammar checking
22644
22684
if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node);
0 commit comments