@@ -911,6 +911,7 @@ namespace ts {
911
911
const sharedFlowNodes: FlowNode[] = [];
912
912
const sharedFlowTypes: FlowType[] = [];
913
913
const flowNodeReachable: (boolean | undefined)[] = [];
914
+ const flowNodePostSuper: (boolean | undefined)[] = [];
914
915
const potentialThisCollisions: Node[] = [];
915
916
const potentialNewTargetCollisions: Node[] = [];
916
917
const potentialWeakMapCollisions: Node[] = [];
@@ -5949,6 +5950,7 @@ namespace ts {
5949
5950
}
5950
5951
}
5951
5952
5953
+
5952
5954
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
5953
5955
// or a merge of some number of those.
5954
5956
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6317,7 +6319,10 @@ namespace ts {
6317
6319
const baseTypes = getBaseTypes(classType);
6318
6320
const implementsTypes = getImplementsTypes(classType);
6319
6321
const staticType = getTypeOfSymbol(symbol);
6320
- const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6322
+ const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6323
+ const staticBaseType = isClass
6324
+ ? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6325
+ : anyType;
6321
6326
const heritageClauses = [
6322
6327
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
6323
6328
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6353,7 +6358,17 @@ namespace ts {
6353
6358
const staticMembers = flatMap(
6354
6359
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
6355
6360
p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6356
- const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6361
+ // When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6362
+ // the value is ever initialized with a class or function-like value. For cases where `X` could never be
6363
+ // created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6364
+ const isNonConstructableClassLikeInJsFile =
6365
+ !isClass &&
6366
+ !!symbol.valueDeclaration &&
6367
+ isInJSFile(symbol.valueDeclaration) &&
6368
+ !some(getSignaturesOfType(staticType, SignatureKind.Construct));
6369
+ const constructors = isNonConstructableClassLikeInJsFile ?
6370
+ [createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6371
+ serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6357
6372
for (const c of constructors) {
6358
6373
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
6359
6374
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20134,7 +20149,7 @@ namespace ts {
20134
20149
noCacheCheck = false;
20135
20150
}
20136
20151
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20137
- flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow >flow).antecedent;
20152
+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
20138
20153
}
20139
20154
else if (flags & FlowFlags.Call) {
20140
20155
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20184,6 +20199,51 @@ namespace ts {
20184
20199
}
20185
20200
}
20186
20201
20202
+ // Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
20203
+ // leading to the node.
20204
+ function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
20205
+ while (true) {
20206
+ const flags = flow.flags;
20207
+ if (flags & FlowFlags.Shared) {
20208
+ if (!noCacheCheck) {
20209
+ const id = getFlowNodeId(flow);
20210
+ const postSuper = flowNodePostSuper[id];
20211
+ return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
20212
+ }
20213
+ noCacheCheck = false;
20214
+ }
20215
+ if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
20216
+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
20217
+ }
20218
+ else if (flags & FlowFlags.Call) {
20219
+ if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
20220
+ return true;
20221
+ }
20222
+ flow = (<FlowCall>flow).antecedent;
20223
+ }
20224
+ else if (flags & FlowFlags.BranchLabel) {
20225
+ // A branching point is post-super if every branch is post-super.
20226
+ return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
20227
+ }
20228
+ else if (flags & FlowFlags.LoopLabel) {
20229
+ // A loop is post-super if the control flow path that leads to the top is post-super.
20230
+ flow = (<FlowLabel>flow).antecedents![0];
20231
+ }
20232
+ else if (flags & FlowFlags.ReduceLabel) {
20233
+ const target = (<FlowReduceLabel>flow).target;
20234
+ const saveAntecedents = target.antecedents;
20235
+ target.antecedents = (<FlowReduceLabel>flow).antecedents;
20236
+ const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
20237
+ target.antecedents = saveAntecedents;
20238
+ return result;
20239
+ }
20240
+ else {
20241
+ // Unreachable nodes are considered post-super to silence errors
20242
+ return !!(flags & FlowFlags.Unreachable);
20243
+ }
20244
+ }
20245
+ }
20246
+
20187
20247
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
20188
20248
let key: string | undefined;
20189
20249
let keySet = false;
@@ -21583,31 +21643,10 @@ namespace ts {
21583
21643
}
21584
21644
}
21585
21645
21586
- function findFirstSuperCall(n: Node): SuperCall | undefined {
21587
- if (isSuperCall(n)) {
21588
- return n;
21589
- }
21590
- else if (isFunctionLike(n)) {
21591
- return undefined;
21592
- }
21593
- return forEachChild(n, findFirstSuperCall);
21594
- }
21595
-
21596
- /**
21597
- * Return a cached result if super-statement is already found.
21598
- * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
21599
- *
21600
- * @param constructor constructor-function to look for super statement
21601
- */
21602
- function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined {
21603
- const links = getNodeLinks(constructor);
21604
-
21605
- // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
21606
- if (links.hasSuperCall === undefined) {
21607
- links.superCall = findFirstSuperCall(constructor.body!);
21608
- links.hasSuperCall = links.superCall ? true : false;
21609
- }
21610
- return links.superCall!;
21646
+ function findFirstSuperCall(node: Node): SuperCall | undefined {
21647
+ return isSuperCall(node) ? node :
21648
+ isFunctionLike(node) ? undefined :
21649
+ forEachChild(node, findFirstSuperCall);
21611
21650
}
21612
21651
21613
21652
/**
@@ -21630,17 +21669,7 @@ namespace ts {
21630
21669
// If a containing class does not have extends clause or the class extends null
21631
21670
// skip checking whether super statement is called before "this" accessing.
21632
21671
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21633
- const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21634
-
21635
- // We should give an error in the following cases:
21636
- // - No super-call
21637
- // - "this" is accessing before super-call.
21638
- // i.e super(this)
21639
- // this.x; super();
21640
- // We want to make sure that super-call is done before accessing "this" so that
21641
- // "this" is not accessed as a parameter of the super-call.
21642
- if (!superCall || superCall.end > node.pos) {
21643
- // In ES6, super inside constructor of class-declaration has to precede "this" accessing
21672
+ if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
21644
21673
error(node, diagnosticMessage);
21645
21674
}
21646
21675
}
@@ -21865,7 +21894,8 @@ namespace ts {
21865
21894
function checkSuperExpression(node: Node): Type {
21866
21895
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
21867
21896
21868
- let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21897
+ const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21898
+ let container = immediateContainer;
21869
21899
let needToCaptureLexicalThis = false;
21870
21900
21871
21901
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21901,7 +21931,7 @@ namespace ts {
21901
21931
return errorType;
21902
21932
}
21903
21933
21904
- if (!isCallExpression && container .kind === SyntaxKind.Constructor) {
21934
+ if (!isCallExpression && immediateContainer .kind === SyntaxKind.Constructor) {
21905
21935
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
21906
21936
}
21907
21937
@@ -29898,7 +29928,7 @@ namespace ts {
29898
29928
if (getClassExtendsHeritageElement(containingClassDecl)) {
29899
29929
captureLexicalThis(node.parent, containingClassDecl);
29900
29930
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
29901
- const superCall = getSuperCallInConstructor (node);
29931
+ const superCall = findFirstSuperCall (node.body! );
29902
29932
if (superCall) {
29903
29933
if (classExtendsNull) {
29904
29934
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
0 commit comments