@@ -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[] = [];
@@ -5969,6 +5970,7 @@ namespace ts {
59695970 }
59705971 }
59715972
5973+
59725974 // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59735975 // or a merge of some number of those.
59745976 // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6337,7 +6339,10 @@ namespace ts {
63376339 const baseTypes = getBaseTypes(classType);
63386340 const implementsTypes = getImplementsTypes(classType);
63396341 const staticType = getTypeOfSymbol(symbol);
6340- const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6342+ const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
6343+ const staticBaseType = isClass
6344+ ? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6345+ : anyType;
63416346 const heritageClauses = [
63426347 ...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63436348 ...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6373,7 +6378,17 @@ namespace ts {
63736378 const staticMembers = flatMap(
63746379 filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
63756380 p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType));
6376- const constructors = serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
6381+ // When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether
6382+ // the value is ever initialized with a class or function-like value. For cases where `X` could never be
6383+ // created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable.
6384+ const isNonConstructableClassLikeInJsFile =
6385+ !isClass &&
6386+ !!symbol.valueDeclaration &&
6387+ isInJSFile(symbol.valueDeclaration) &&
6388+ !some(getSignaturesOfType(staticType, SignatureKind.Construct));
6389+ const constructors = isNonConstructableClassLikeInJsFile ?
6390+ [createConstructor(/*decorators*/ undefined, createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] :
6391+ serializeSignatures(SignatureKind.Construct, staticType, baseTypes[0], SyntaxKind.Constructor) as ConstructorDeclaration[];
63776392 for (const c of constructors) {
63786393 // A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
63796394 // `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20193,7 +20208,7 @@ namespace ts {
2019320208 noCacheCheck = false;
2019420209 }
2019520210 if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20196- flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow >flow).antecedent;
20211+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
2019720212 }
2019820213 else if (flags & FlowFlags.Call) {
2019920214 const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20243,6 +20258,51 @@ namespace ts {
2024320258 }
2024420259 }
2024520260
20261+ // Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
20262+ // leading to the node.
20263+ function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
20264+ while (true) {
20265+ const flags = flow.flags;
20266+ if (flags & FlowFlags.Shared) {
20267+ if (!noCacheCheck) {
20268+ const id = getFlowNodeId(flow);
20269+ const postSuper = flowNodePostSuper[id];
20270+ return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
20271+ }
20272+ noCacheCheck = false;
20273+ }
20274+ if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
20275+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
20276+ }
20277+ else if (flags & FlowFlags.Call) {
20278+ if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
20279+ return true;
20280+ }
20281+ flow = (<FlowCall>flow).antecedent;
20282+ }
20283+ else if (flags & FlowFlags.BranchLabel) {
20284+ // A branching point is post-super if every branch is post-super.
20285+ return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
20286+ }
20287+ else if (flags & FlowFlags.LoopLabel) {
20288+ // A loop is post-super if the control flow path that leads to the top is post-super.
20289+ flow = (<FlowLabel>flow).antecedents![0];
20290+ }
20291+ else if (flags & FlowFlags.ReduceLabel) {
20292+ const target = (<FlowReduceLabel>flow).target;
20293+ const saveAntecedents = target.antecedents;
20294+ target.antecedents = (<FlowReduceLabel>flow).antecedents;
20295+ const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
20296+ target.antecedents = saveAntecedents;
20297+ return result;
20298+ }
20299+ else {
20300+ // Unreachable nodes are considered post-super to silence errors
20301+ return !!(flags & FlowFlags.Unreachable);
20302+ }
20303+ }
20304+ }
20305+
2024620306 function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
2024720307 let key: string | undefined;
2024820308 let keySet = false;
@@ -21642,31 +21702,10 @@ namespace ts {
2164221702 }
2164321703 }
2164421704
21645- function findFirstSuperCall(n: Node): SuperCall | undefined {
21646- if (isSuperCall(n)) {
21647- return n;
21648- }
21649- else if (isFunctionLike(n)) {
21650- return undefined;
21651- }
21652- return forEachChild(n, findFirstSuperCall);
21653- }
21654-
21655- /**
21656- * Return a cached result if super-statement is already found.
21657- * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
21658- *
21659- * @param constructor constructor-function to look for super statement
21660- */
21661- function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined {
21662- const links = getNodeLinks(constructor);
21663-
21664- // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
21665- if (links.hasSuperCall === undefined) {
21666- links.superCall = findFirstSuperCall(constructor.body!);
21667- links.hasSuperCall = links.superCall ? true : false;
21668- }
21669- return links.superCall!;
21705+ function findFirstSuperCall(node: Node): SuperCall | undefined {
21706+ return isSuperCall(node) ? node :
21707+ isFunctionLike(node) ? undefined :
21708+ forEachChild(node, findFirstSuperCall);
2167021709 }
2167121710
2167221711 /**
@@ -21689,17 +21728,7 @@ namespace ts {
2168921728 // If a containing class does not have extends clause or the class extends null
2169021729 // skip checking whether super statement is called before "this" accessing.
2169121730 if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21692- const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21693-
21694- // We should give an error in the following cases:
21695- // - No super-call
21696- // - "this" is accessing before super-call.
21697- // i.e super(this)
21698- // this.x; super();
21699- // We want to make sure that super-call is done before accessing "this" so that
21700- // "this" is not accessed as a parameter of the super-call.
21701- if (!superCall || superCall.end > node.pos) {
21702- // In ES6, super inside constructor of class-declaration has to precede "this" accessing
21731+ if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
2170321732 error(node, diagnosticMessage);
2170421733 }
2170521734 }
@@ -21924,7 +21953,8 @@ namespace ts {
2192421953 function checkSuperExpression(node: Node): Type {
2192521954 const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
2192621955
21927- let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21956+ const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21957+ let container = immediateContainer;
2192821958 let needToCaptureLexicalThis = false;
2192921959
2193021960 // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21960,7 +21990,7 @@ namespace ts {
2196021990 return errorType;
2196121991 }
2196221992
21963- if (!isCallExpression && container .kind === SyntaxKind.Constructor) {
21993+ if (!isCallExpression && immediateContainer .kind === SyntaxKind.Constructor) {
2196421994 checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
2196521995 }
2196621996
@@ -22498,6 +22528,10 @@ namespace ts {
2249822528 }
2249922529 }
2250022530
22531+ function isCircularMappedProperty(symbol: Symbol) {
22532+ return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(<MappedSymbol>symbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0);
22533+ }
22534+
2250122535 function getTypeOfPropertyOfContextualType(type: Type, name: __String) {
2250222536 return mapType(type, t => {
2250322537 if (isGenericMappedType(t)) {
@@ -22511,7 +22545,7 @@ namespace ts {
2251122545 else if (t.flags & TypeFlags.StructuredType) {
2251222546 const prop = getPropertyOfType(t, name);
2251322547 if (prop) {
22514- return getTypeOfSymbol(prop);
22548+ return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop);
2251522549 }
2251622550 if (isTupleType(t)) {
2251722551 const restType = getRestTypeOfTupleType(t);
@@ -29999,7 +30033,7 @@ namespace ts {
2999930033 if (getClassExtendsHeritageElement(containingClassDecl)) {
3000030034 captureLexicalThis(node.parent, containingClassDecl);
3000130035 const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
30002- const superCall = getSuperCallInConstructor (node);
30036+ const superCall = findFirstSuperCall (node.body! );
3000330037 if (superCall) {
3000430038 if (classExtendsNull) {
3000530039 error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
0 commit comments