@@ -911,6 +911,7 @@ namespace ts {
911911 const sharedFlowNodes: FlowNode[] = [];
912912 const sharedFlowTypes: FlowType[] = [];
913913 const flowNodeReachable: (boolean | undefined)[] = [];
914+ const flowNodePostSuper: (boolean | undefined)[] = [];
914915 const potentialThisCollisions: Node[] = [];
915916 const potentialNewTargetCollisions: Node[] = [];
916917 const potentialWeakMapCollisions: Node[] = [];
@@ -5952,6 +5953,7 @@ namespace ts {
59525953 }
59535954 }
59545955
5956+
59555957 // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59565958 // or a merge of some number of those.
59575959 // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6320,7 +6322,10 @@ namespace ts {
63206322 const baseTypes = getBaseTypes(classType);
63216323 const implementsTypes = getImplementsTypes(classType);
63226324 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;
63246329 const heritageClauses = [
63256330 ...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63266331 ...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6356,7 +6361,17 @@ namespace ts {
63566361 const staticMembers = flatMap(
63576362 filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63586363 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[];
63606375 for (const c of constructors) {
63616376 // A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63626377 // `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20137,7 +20152,7 @@ namespace ts {
2013720152 noCacheCheck = false;
2013820153 }
2013920154 if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20140- flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow >flow).antecedent;
20155+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
2014120156 }
2014220157 else if (flags & FlowFlags.Call) {
2014320158 const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20187,6 +20202,51 @@ namespace ts {
2018720202 }
2018820203 }
2018920204
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+
2019020250 function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
2019120251 let key: string | undefined;
2019220252 let keySet = false;
@@ -21586,31 +21646,10 @@ namespace ts {
2158621646 }
2158721647 }
2158821648
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);
2161421653 }
2161521654
2161621655 /**
@@ -21633,17 +21672,7 @@ namespace ts {
2163321672 // If a containing class does not have extends clause or the class extends null
2163421673 // skip checking whether super statement is called before "this" accessing.
2163521674 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)) {
2164721676 error(node, diagnosticMessage);
2164821677 }
2164921678 }
@@ -21868,7 +21897,8 @@ namespace ts {
2186821897 function checkSuperExpression(node: Node): Type {
2186921898 const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2187021899
21871- let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21900+ const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21901+ let container = immediateContainer;
2187221902 let needToCaptureLexicalThis = false;
2187321903
2187421904 // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21904,7 +21934,7 @@ namespace ts {
2190421934 return errorType;
2190521935 }
2190621936
21907- if (!isCallExpression && container .kind === SyntaxKind.Constructor) {
21937+ if (!isCallExpression && immediateContainer .kind === SyntaxKind.Constructor) {
2190821938 checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
2190921939 }
2191021940
@@ -29901,7 +29931,7 @@ namespace ts {
2990129931 if (getClassExtendsHeritageElement(containingClassDecl)) {
2990229932 captureLexicalThis(node.parent, containingClassDecl);
2990329933 const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
29904- const superCall = getSuperCallInConstructor (node);
29934+ const superCall = findFirstSuperCall (node.body! );
2990529935 if (superCall) {
2990629936 if (classExtendsNull) {
2990729937 error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
0 commit comments