diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 631960f24ba..32182081c4d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11925,7 +11925,7 @@ namespace ts { // on empty arrays are possible without implicit any errors and new element types can be inferred without // type mismatch errors. const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType); - if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { + if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { return declaredType; } return resultType; @@ -22072,6 +22072,7 @@ namespace ts { if (produceDiagnostics) { checkIndexConstraints(type); checkTypeForDuplicateIndexSignatures(node); + checkPropertyInitialization(node); } } @@ -22228,6 +22229,33 @@ namespace ts { return ok; } + function checkPropertyInitialization(node: ClassLikeDeclaration) { + if (!strictNullChecks || node.flags & NodeFlags.Ambient) { + return; + } + const constructor = findConstructorDeclaration(node); + for (const member of node.members) { + if (member.kind === SyntaxKind.PropertyDeclaration && !(member).initializer) { + const propName = (member).name; + if (isIdentifier(propName)) { + const type = getTypeOfSymbol(getSymbolOfNode(member)); + if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) { + if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) { + error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName)); + } + } + } + } + } + } + + function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) { + const reference = createPropertyAccess(createThis(), propName); + reference.flowNode = constructor.returnFlowNode; + const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType)); + return !(getFalsyFlags(flowType) & TypeFlags.Undefined); + } + function checkInterfaceDeclaration(node: InterfaceDeclaration) { // Grammar checking if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a552122609a..df6accb443d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1928,6 +1928,10 @@ "category": "Error", "code": 2563 }, + "Property '{0}' has no initializer and is not definitely assigned in the constructor.": { + "category": "Error", + "code": 2564 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600