Add definite assignment checks for property accesses in constructor body

This commit is contained in:
Anders Hejlsberg 2017-11-18 11:32:17 -08:00
parent dccf57f107
commit 0fbf36c2fd
2 changed files with 31 additions and 2 deletions

View file

@ -15604,7 +15604,26 @@ namespace ts {
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
}
const flowType = getFlowTypeOfReference(node, propType);
// If strict null checks and strict property initialization checks are enabled, if we have
// a this.xxx property access, if the property is an instance property without an initializer,
// and if we are in a constructor of the same class as the property declaration, assume that
// the property is uninitialized at the top of the control flow.
let assumeUninitialized = false;
if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) {
const declaration = prop && prop.valueDeclaration;
if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
const flowContainer = getControlFlowContainer(node);
if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) {
assumeUninitialized = true;
}
}
}
const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop));
// Return the declared type to reduce follow-on errors
return propType;
}
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
@ -22633,7 +22652,7 @@ namespace ts {
}
const constructor = findConstructorDeclaration(node);
for (const member of node.members) {
if (member.kind === SyntaxKind.PropertyDeclaration && !hasModifier(member, ModifierFlags.Static | ModifierFlags.Abstract) && !(<PropertyDeclaration>member).initializer) {
if (isInstancePropertyWithoutInitializer(member)) {
const propName = (<PropertyDeclaration>member).name;
if (isIdentifier(propName)) {
const type = getTypeOfSymbol(getSymbolOfNode(member));
@ -22647,6 +22666,12 @@ namespace ts {
}
}
function isInstancePropertyWithoutInitializer(node: Node) {
return node.kind === SyntaxKind.PropertyDeclaration &&
!hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) &&
!(<PropertyDeclaration>node).initializer;
}
function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
const reference = createPropertyAccess(createThis(), propName);
reference.flowNode = constructor.returnFlowNode;

View file

@ -1956,6 +1956,10 @@
"category": "Error",
"code": 2564
},
"Property '{0}' is used before being assigned.": {
"category": "Error",
"code": 2565
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600