Skip to content

Commit 418274e

Browse files
committed
Merge branch 'master' into jsdoc-values-as-namespaces
2 parents b1c735f + 9abb72d commit 418274e

56 files changed

Lines changed: 39665 additions & 591 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/compiler/binder.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,16 +517,15 @@ namespace ts {
517517
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && !!getImmediatelyInvokedFunctionExpression(node);
518518
// A non-async IIFE is considered part of the containing control flow. Return statements behave
519519
// similarly to break statements that exit to a label just past the statement body.
520-
if (isIIFE) {
521-
currentReturnTarget = createBranchLabel();
522-
}
523-
else {
520+
if (!isIIFE) {
524521
currentFlow = { flags: FlowFlags.Start };
525522
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
526523
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
527524
}
528-
currentReturnTarget = undefined;
529525
}
526+
// We create a return control flow graph for IIFEs and constructors. For constructors
527+
// we use the return control flow graph in strict property intialization checks.
528+
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor ? createBranchLabel() : undefined;
530529
currentBreakTarget = undefined;
531530
currentContinueTarget = undefined;
532531
activeLabels = undefined;
@@ -541,11 +540,14 @@ namespace ts {
541540
if (node.kind === SyntaxKind.SourceFile) {
542541
node.flags |= emitFlags;
543542
}
544-
if (isIIFE) {
543+
if (currentReturnTarget) {
545544
addAntecedent(currentReturnTarget, currentFlow);
546545
currentFlow = finishFlowLabel(currentReturnTarget);
546+
if (node.kind === SyntaxKind.Constructor) {
547+
(<ConstructorDeclaration>node).returnFlowNode = currentFlow;
548+
}
547549
}
548-
else {
550+
if (!isIIFE) {
549551
currentFlow = saveCurrentFlow;
550552
}
551553
currentBreakTarget = saveBreakTarget;

src/compiler/checker.ts

Lines changed: 99 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -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 {

src/compiler/commandLineParser.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,13 @@ namespace ts {
277277
category: Diagnostics.Strict_Type_Checking_Options,
278278
description: Diagnostics.Enable_strict_checking_of_function_types
279279
},
280+
{
281+
name: "strictPropertyInitialization",
282+
type: "boolean",
283+
showInSimplifiedHelpView: true,
284+
category: Diagnostics.Strict_Type_Checking_Options,
285+
description: Diagnostics.Enable_strict_checking_of_property_initialization_in_classes
286+
},
280287
{
281288
name: "noImplicitThis",
282289
type: "boolean",

src/compiler/core.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,10 +1260,12 @@ namespace ts {
12601260
return result;
12611261
}
12621262

1263-
export function arrayToNumericMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => number): T[] {
1264-
const result: T[] = [];
1263+
export function arrayToNumericMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => number): T[];
1264+
export function arrayToNumericMap<T, V>(array: ReadonlyArray<T>, makeKey: (value: T) => number, makeValue: (value: T) => V): V[];
1265+
export function arrayToNumericMap<T, V>(array: ReadonlyArray<T>, makeKey: (value: T) => number, makeValue?: (value: T) => V): V[] {
1266+
const result: V[] = [];
12651267
for (const value of array) {
1266-
result[makeKey(value)] = value;
1268+
result[makeKey(value)] = makeValue ? makeValue(value) : value as any as V;
12671269
}
12681270
return result;
12691271
}
@@ -1967,7 +1969,7 @@ namespace ts {
19671969
: moduleKind === ModuleKind.System;
19681970
}
19691971

1970-
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "alwaysStrict";
1972+
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict";
19711973

19721974
export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {
19731975
return compilerOptions[flag] === undefined ? compilerOptions.strict : compilerOptions[flag];

0 commit comments

Comments
 (0)