@@ -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[] = [];
@@ -5969,6 +5970,7 @@ namespace ts {
5969
5970
}
5970
5971
}
5971
5972
5973
+
5972
5974
// Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
5973
5975
// or a merge of some number of those.
5974
5976
// An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -6337,7 +6339,10 @@ namespace ts {
6337
6339
const baseTypes = getBaseTypes(classType);
6338
6340
const implementsTypes = getImplementsTypes(classType);
6339
6341
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;
6341
6346
const heritageClauses = [
6342
6347
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
6343
6348
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
@@ -6373,7 +6378,17 @@ namespace ts {
6373
6378
const staticMembers = flatMap(
6374
6379
filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)),
6375
6380
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[];
6377
6392
for (const c of constructors) {
6378
6393
// A constructor's return type and type parameters are supposed to be controlled by the enclosing class declaration
6379
6394
// `signatureToSignatureDeclarationHelper` appends them regardless, so for now we delete them here
@@ -20193,7 +20208,7 @@ namespace ts {
20193
20208
noCacheCheck = false;
20194
20209
}
20195
20210
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20196
- flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow >flow).antecedent;
20211
+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
20197
20212
}
20198
20213
else if (flags & FlowFlags.Call) {
20199
20214
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20243,6 +20258,51 @@ namespace ts {
20243
20258
}
20244
20259
}
20245
20260
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
+
20246
20306
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
20247
20307
let key: string | undefined;
20248
20308
let keySet = false;
@@ -21642,31 +21702,10 @@ namespace ts {
21642
21702
}
21643
21703
}
21644
21704
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);
21670
21709
}
21671
21710
21672
21711
/**
@@ -21689,17 +21728,7 @@ namespace ts {
21689
21728
// If a containing class does not have extends clause or the class extends null
21690
21729
// skip checking whether super statement is called before "this" accessing.
21691
21730
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)) {
21703
21732
error(node, diagnosticMessage);
21704
21733
}
21705
21734
}
@@ -21924,7 +21953,8 @@ namespace ts {
21924
21953
function checkSuperExpression(node: Node): Type {
21925
21954
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
21926
21955
21927
- let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21956
+ const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21957
+ let container = immediateContainer;
21928
21958
let needToCaptureLexicalThis = false;
21929
21959
21930
21960
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21960,7 +21990,7 @@ namespace ts {
21960
21990
return errorType;
21961
21991
}
21962
21992
21963
- if (!isCallExpression && container .kind === SyntaxKind.Constructor) {
21993
+ if (!isCallExpression && immediateContainer .kind === SyntaxKind.Constructor) {
21964
21994
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
21965
21995
}
21966
21996
@@ -22498,6 +22528,10 @@ namespace ts {
22498
22528
}
22499
22529
}
22500
22530
22531
+ function isCircularMappedProperty(symbol: Symbol) {
22532
+ return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(<MappedSymbol>symbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0);
22533
+ }
22534
+
22501
22535
function getTypeOfPropertyOfContextualType(type: Type, name: __String) {
22502
22536
return mapType(type, t => {
22503
22537
if (isGenericMappedType(t)) {
@@ -22511,7 +22545,7 @@ namespace ts {
22511
22545
else if (t.flags & TypeFlags.StructuredType) {
22512
22546
const prop = getPropertyOfType(t, name);
22513
22547
if (prop) {
22514
- return getTypeOfSymbol(prop);
22548
+ return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop);
22515
22549
}
22516
22550
if (isTupleType(t)) {
22517
22551
const restType = getRestTypeOfTupleType(t);
@@ -29999,7 +30033,7 @@ namespace ts {
29999
30033
if (getClassExtendsHeritageElement(containingClassDecl)) {
30000
30034
captureLexicalThis(node.parent, containingClassDecl);
30001
30035
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
30002
- const superCall = getSuperCallInConstructor (node);
30036
+ const superCall = findFirstSuperCall (node.body! );
30003
30037
if (superCall) {
30004
30038
if (classExtendsNull) {
30005
30039
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
0 commit comments