Skip to content

Commit 5f6d7f3

Browse files
committed
Add strict property initialization checks
1 parent f1762a0 commit 5f6d7f3

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

src/compiler/checker.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11925,7 +11925,7 @@ namespace ts {
1192511925
// on empty arrays are possible without implicit any errors and new element types can be inferred without
1192611926
// type mismatch errors.
1192711927
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
11928-
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
11928+
if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
1192911929
return declaredType;
1193011930
}
1193111931
return resultType;
@@ -22072,6 +22072,7 @@ namespace ts {
2207222072
if (produceDiagnostics) {
2207322073
checkIndexConstraints(type);
2207422074
checkTypeForDuplicateIndexSignatures(node);
22075+
checkPropertyInitialization(node);
2207522076
}
2207622077
}
2207722078

@@ -22228,6 +22229,33 @@ namespace ts {
2222822229
return ok;
2222922230
}
2223022231

22232+
function checkPropertyInitialization(node: ClassLikeDeclaration) {
22233+
if (!strictNullChecks || node.flags & NodeFlags.Ambient) {
22234+
return;
22235+
}
22236+
const constructor = findConstructorDeclaration(node);
22237+
for (const member of node.members) {
22238+
if (member.kind === SyntaxKind.PropertyDeclaration && !(<PropertyDeclaration>member).initializer) {
22239+
const propName = (<PropertyDeclaration>member).name;
22240+
if (isIdentifier(propName)) {
22241+
const type = getTypeOfSymbol(getSymbolOfNode(member));
22242+
if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) {
22243+
if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
22244+
error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName));
22245+
}
22246+
}
22247+
}
22248+
}
22249+
}
22250+
}
22251+
22252+
function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
22253+
const reference = createPropertyAccess(createThis(), propName);
22254+
reference.flowNode = constructor.returnFlowNode;
22255+
const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
22256+
return !(getFalsyFlags(flowType) & TypeFlags.Undefined);
22257+
}
22258+
2223122259
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
2223222260
// Grammar checking
2223322261
if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node);

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,10 @@
19281928
"category": "Error",
19291929
"code": 2563
19301930
},
1931+
"Property '{0}' has no initializer and is not definitely assigned in the constructor.": {
1932+
"category": "Error",
1933+
"code": 2564
1934+
},
19311935
"JSX element attributes type '{0}' may not be a union type.": {
19321936
"category": "Error",
19331937
"code": 2600

0 commit comments

Comments
 (0)