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