@@ -69,6 +69,7 @@ namespace ts {
6969 const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
7070 const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
7171 const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
72+ const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
7273 const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
7374 const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
7475
@@ -8879,13 +8880,15 @@ namespace ts {
88798880 // An object type S is considered to be derived from an object type T if
88808881 // S is a union type and every constituent of S is derived from T,
88818882 // T is a union type and S is derived from at least one constituent of T, or
8883+ // S is a type variable with a base constraint that is derived from T,
88828884 // T is one of the global types Object and Function and S is a subtype of T, or
88838885 // T occurs directly or indirectly in an 'extends' clause of S.
88848886 // Note that this check ignores type parameters and only considers the
88858887 // inheritance hierarchy.
88868888 function isTypeDerivedFrom(source: Type, target: Type): boolean {
88878889 return source.flags & TypeFlags.Union ? every((<UnionType>source).types, t => isTypeDerivedFrom(t, target)) :
88888890 target.flags & TypeFlags.Union ? some((<UnionType>target).types, t => isTypeDerivedFrom(source, t)) :
8891+ source.flags & TypeFlags.TypeVariable ? isTypeDerivedFrom(getBaseConstraintOfType(source) || emptyObjectType, target) :
88898892 target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) :
88908893 hasBaseType(source, getTargetType(target));
88918894 }
@@ -12294,7 +12297,7 @@ namespace ts {
1229412297 // on empty arrays are possible without implicit any errors and new element types can be inferred without
1229512298 // type mismatch errors.
1229612299 const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
12297- if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
12300+ if (reference.parent && reference.parent .kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
1229812301 return declaredType;
1229912302 }
1230012303 return resultType;
@@ -13131,8 +13134,10 @@ namespace ts {
1313113134 // the entire control flow graph from the variable's declaration (i.e. when the flow container and
1313213135 // declaration container are the same).
1313313136 const assumeInitialized = isParameter || isAlias || isOuterVariable ||
13134- type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
13137+ type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 ||
13138+ isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
1313513139 node.parent.kind === SyntaxKind.NonNullExpression ||
13140+ declaration.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>declaration).exclamationToken ||
1313613141 declaration.flags & NodeFlags.Ambient;
1313713142 const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) :
1313813143 type === autoType || type === autoArrayType ? undefinedType :
@@ -15572,63 +15577,68 @@ namespace ts {
1557215577 }
1557315578
1557415579 function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
15575- const type = checkNonNullExpression(left);
15576- if (isTypeAny(type) || type === silentNeverType) {
15577- return type;
15578- }
15579-
15580- const apparentType = getApparentType(getWidenedType(type));
15581- if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
15582- // handle cases when type is Type parameter with invalid or any constraint
15580+ let propType: Type;
15581+ const leftType = checkNonNullExpression(left);
15582+ const apparentType = getApparentType(getWidenedType(leftType));
15583+ if (isTypeAny(apparentType) || apparentType === silentNeverType) {
1558315584 return apparentType;
1558415585 }
15586+ const assignmentKind = getAssignmentTargetKind(node);
1558515587 const prop = getPropertyOfType(apparentType, right.escapedText);
1558615588 if (!prop) {
1558715589 const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
15588- if (indexInfo && indexInfo.type) {
15589- if (indexInfo.isReadonly && (isAssignmentTarget( node) || isDeleteTarget(node) )) {
15590- error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType) );
15590+ if (!( indexInfo && indexInfo.type) ) {
15591+ if (right.escapedText && !checkAndReportErrorForExtendingInterface( node)) {
15592+ reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType );
1559115593 }
15592- return getFlowTypeOfPropertyAccess(node, /*prop*/ undefined, indexInfo.type, getAssignmentTargetKind(node)) ;
15594+ return unknownType ;
1559315595 }
15594- if (right.escapedText && !checkAndReportErrorForExtendingInterface( node)) {
15595- reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type );
15596+ if (indexInfo.isReadonly && (isAssignmentTarget( node) || isDeleteTarget(node) )) {
15597+ error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString( apparentType) );
1559615598 }
15597- return unknownType ;
15599+ propType = indexInfo.type ;
1559815600 }
15599-
15600- checkPropertyNotUsedBeforeDeclaration(prop, node, right);
15601-
15602- markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
15603-
15604- getNodeLinks(node).resolvedSymbol = prop;
15605-
15606- checkPropertyAccessibility(node, left, apparentType, prop);
15607-
15608- const propType = getDeclaredOrApparentType(prop, node);
15609- const assignmentKind = getAssignmentTargetKind(node);
15610-
15611- if (assignmentKind) {
15612- if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
15613- error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
15614- return unknownType;
15601+ else {
15602+ checkPropertyNotUsedBeforeDeclaration(prop, node, right);
15603+ markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
15604+ getNodeLinks(node).resolvedSymbol = prop;
15605+ checkPropertyAccessibility(node, left, apparentType, prop);
15606+ if (assignmentKind) {
15607+ if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
15608+ error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
15609+ return unknownType;
15610+ }
1561515611 }
15612+ propType = getDeclaredOrApparentType(prop, node);
1561615613 }
15617- return getFlowTypeOfPropertyAccess(node, prop, propType, assignmentKind);
15618- }
15619-
15620- /**
15621- * Only compute control flow type if this is a property access expression that isn't an
15622- * assignment target, and the referenced property was declared as a variable, property,
15623- * accessor, or optional method.
15624- */
15625- function getFlowTypeOfPropertyAccess(node: PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, type: Type, assignmentKind: AssignmentKind) {
15614+ // Only compute control flow type if this is a property access expression that isn't an
15615+ // assignment target, and the referenced property was declared as a variable, property,
15616+ // accessor, or optional method.
1562615617 if (node.kind !== SyntaxKind.PropertyAccessExpression ||
1562715618 assignmentKind === AssignmentKind.Definite ||
15628- prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && type.flags & TypeFlags.Union)) {
15629- return type;
15619+ prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
15620+ return propType;
15621+ }
15622+ // If strict null checks and strict property initialization checks are enabled, if we have
15623+ // a this.xxx property access, if the property is an instance property without an initializer,
15624+ // and if we are in a constructor of the same class as the property declaration, assume that
15625+ // the property is uninitialized at the top of the control flow.
15626+ let assumeUninitialized = false;
15627+ if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) {
15628+ const declaration = prop && prop.valueDeclaration;
15629+ if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
15630+ const flowContainer = getControlFlowContainer(node);
15631+ if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) {
15632+ assumeUninitialized = true;
15633+ }
15634+ }
15635+ }
15636+ const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
15637+ if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
15638+ error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop));
15639+ // Return the declared type to reduce follow-on errors
15640+ return propType;
1563015641 }
15631- const flowType = getFlowTypeOfReference(node, type);
1563215642 return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1563315643 }
1563415644
@@ -22494,6 +22504,7 @@ namespace ts {
2249422504 if (produceDiagnostics) {
2249522505 checkIndexConstraints(type);
2249622506 checkTypeForDuplicateIndexSignatures(node);
22507+ checkPropertyInitialization(node);
2249722508 }
2249822509 }
2249922510
@@ -22650,6 +22661,40 @@ namespace ts {
2265022661 return ok;
2265122662 }
2265222663
22664+ function checkPropertyInitialization(node: ClassLikeDeclaration) {
22665+ if (!strictNullChecks || !strictPropertyInitialization || node.flags & NodeFlags.Ambient) {
22666+ return;
22667+ }
22668+ const constructor = findConstructorDeclaration(node);
22669+ for (const member of node.members) {
22670+ if (isInstancePropertyWithoutInitializer(member)) {
22671+ const propName = (<PropertyDeclaration>member).name;
22672+ if (isIdentifier(propName)) {
22673+ const type = getTypeOfSymbol(getSymbolOfNode(member));
22674+ if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) {
22675+ if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
22676+ error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName));
22677+ }
22678+ }
22679+ }
22680+ }
22681+ }
22682+ }
22683+
22684+ function isInstancePropertyWithoutInitializer(node: Node) {
22685+ return node.kind === SyntaxKind.PropertyDeclaration &&
22686+ !hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) &&
22687+ !(<PropertyDeclaration>node).exclamationToken &&
22688+ !(<PropertyDeclaration>node).initializer;
22689+ }
22690+
22691+ function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
22692+ const reference = createPropertyAccess(createThis(), propName);
22693+ reference.flowNode = constructor.returnFlowNode;
22694+ const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
22695+ return !(getFalsyFlags(flowType) & TypeFlags.Undefined);
22696+ }
22697+
2265322698 function checkInterfaceDeclaration(node: InterfaceDeclaration) {
2265422699 // Grammar checking
2265522700 if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node);
@@ -26072,6 +26117,10 @@ namespace ts {
2607226117 }
2607326118 }
2607426119
26120+ if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) {
26121+ return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context);
26122+ }
26123+
2607526124 if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ESNext && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
2607626125 !(node.parent.parent.flags & NodeFlags.Ambient) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
2607726126 checkESModuleMarker(node.name);
@@ -26235,6 +26284,11 @@ namespace ts {
2623526284 if (node.flags & NodeFlags.Ambient && node.initializer) {
2623626285 return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
2623726286 }
26287+
26288+ if (node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer ||
26289+ node.flags & NodeFlags.Ambient || hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) {
26290+ return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context);
26291+ }
2623826292 }
2623926293
2624026294 function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean {
0 commit comments