@@ -729,6 +729,7 @@ namespace ts {
729
729
isDeclarationVisible,
730
730
isPropertyAccessible,
731
731
getTypeOnlyAliasDeclaration,
732
+ getMemberOverrideModifierStatus,
732
733
};
733
734
734
735
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
@@ -38469,7 +38470,7 @@ namespace ts {
38469
38470
}
38470
38471
}
38471
38472
38472
- checkMembersForMissingOverrideModifier (node, type, typeWithThis, staticType);
38473
+ checkMembersForOverrideModifier (node, type, typeWithThis, staticType);
38473
38474
38474
38475
const implementedTypeNodes = getEffectiveImplementsTypeNodes(node);
38475
38476
if (implementedTypeNodes) {
@@ -38506,8 +38507,7 @@ namespace ts {
38506
38507
}
38507
38508
}
38508
38509
38509
- function checkMembersForMissingOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type, staticType: ObjectType) {
38510
- const nodeInAmbientContext = !!(node.flags & NodeFlags.Ambient);
38510
+ function checkMembersForOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type, staticType: ObjectType) {
38511
38511
const baseTypeNode = getEffectiveBaseTypeNode(node);
38512
38512
const baseTypes = baseTypeNode && getBaseTypes(type);
38513
38513
const baseWithThis = baseTypes?.length ? getTypeWithThisArgument(first(baseTypes), type.thisType) : undefined;
@@ -38521,77 +38521,163 @@ namespace ts {
38521
38521
if (isConstructorDeclaration(member)) {
38522
38522
forEach(member.parameters, param => {
38523
38523
if (isParameterPropertyDeclaration(param, member)) {
38524
- checkClassMember(param, /*memberIsParameterProperty*/ true);
38524
+ checkExistingMemberForOverrideModifier(
38525
+ node,
38526
+ staticType,
38527
+ baseStaticType,
38528
+ baseWithThis,
38529
+ type,
38530
+ typeWithThis,
38531
+ param,
38532
+ /* memberIsParameterProperty */ true
38533
+ );
38525
38534
}
38526
38535
});
38527
38536
}
38528
- checkClassMember(member);
38537
+ checkExistingMemberForOverrideModifier(
38538
+ node,
38539
+ staticType,
38540
+ baseStaticType,
38541
+ baseWithThis,
38542
+ type,
38543
+ typeWithThis,
38544
+ member,
38545
+ /* memberIsParameterProperty */ false,
38546
+ );
38529
38547
}
38548
+ }
38530
38549
38531
- function checkClassMember(member: ClassElement | ParameterPropertyDeclaration, memberIsParameterProperty?: boolean) {
38532
- const hasOverride = hasOverrideModifier(member);
38533
- const hasStatic = isStatic(member);
38534
- const isJs = isInJSFile(member);
38535
- if (baseWithThis && (hasOverride || compilerOptions.noImplicitOverride)) {
38536
- const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member);
38537
- if (!declaredProp) {
38538
- return;
38539
- }
38540
-
38541
- const thisType = hasStatic ? staticType : typeWithThis;
38542
- const baseType = hasStatic ? baseStaticType : baseWithThis;
38543
- const prop = getPropertyOfType(thisType, declaredProp.escapedName);
38544
- const baseProp = getPropertyOfType(baseType, declaredProp.escapedName);
38550
+ /**
38551
+ * @param member Existing member node to be checked.
38552
+ * Note: `member` cannot be a synthetic node.
38553
+ */
38554
+ function checkExistingMemberForOverrideModifier(
38555
+ node: ClassLikeDeclaration,
38556
+ staticType: ObjectType,
38557
+ baseStaticType: Type,
38558
+ baseWithThis: Type | undefined,
38559
+ type: InterfaceType,
38560
+ typeWithThis: Type,
38561
+ member: ClassElement | ParameterPropertyDeclaration,
38562
+ memberIsParameterProperty: boolean,
38563
+ reportErrors = true,
38564
+ ): MemberOverrideStatus {
38565
+ const declaredProp = member.name
38566
+ && getSymbolAtLocation(member.name)
38567
+ || getSymbolAtLocation(member);
38568
+ if (!declaredProp) {
38569
+ return MemberOverrideStatus.Ok;
38570
+ }
38571
+
38572
+ return checkMemberForOverrideModifier(
38573
+ node,
38574
+ staticType,
38575
+ baseStaticType,
38576
+ baseWithThis,
38577
+ type,
38578
+ typeWithThis,
38579
+ hasOverrideModifier(member),
38580
+ hasAbstractModifier(member),
38581
+ isStatic(member),
38582
+ memberIsParameterProperty,
38583
+ symbolName(declaredProp),
38584
+ reportErrors ? member : undefined,
38585
+ );
38586
+ }
38545
38587
38546
- const baseClassName = typeToString(baseWithThis);
38547
- if (prop && !baseProp && hasOverride) {
38548
- const suggestion = getSuggestedSymbolForNonexistentClassMember(symbolName(declaredProp), baseType);
38588
+ /**
38589
+ * Checks a class member declaration for either a missing or an invalid `override` modifier.
38590
+ * Note: this function can be used for speculative checking,
38591
+ * i.e. checking a member that does not yet exist in the program.
38592
+ * An example of that would be to call this function in a completions scenario,
38593
+ * when offering a method declaration as completion.
38594
+ * @param errorNode The node where we should report an error, or undefined if we should not report errors.
38595
+ */
38596
+ function checkMemberForOverrideModifier(
38597
+ node: ClassLikeDeclaration,
38598
+ staticType: ObjectType,
38599
+ baseStaticType: Type,
38600
+ baseWithThis: Type | undefined,
38601
+ type: InterfaceType,
38602
+ typeWithThis: Type,
38603
+ memberHasOverrideModifier: boolean,
38604
+ memberHasAbstractModifier: boolean,
38605
+ memberIsStatic: boolean,
38606
+ memberIsParameterProperty: boolean,
38607
+ memberName: string,
38608
+ errorNode?: Node,
38609
+ ): MemberOverrideStatus {
38610
+ const isJs = isInJSFile(node);
38611
+ const nodeInAmbientContext = !!(node.flags & NodeFlags.Ambient);
38612
+ if (baseWithThis && (memberHasOverrideModifier || compilerOptions.noImplicitOverride)) {
38613
+ const memberEscapedName = escapeLeadingUnderscores(memberName);
38614
+ const thisType = memberIsStatic ? staticType : typeWithThis;
38615
+ const baseType = memberIsStatic ? baseStaticType : baseWithThis;
38616
+ const prop = getPropertyOfType(thisType, memberEscapedName);
38617
+ const baseProp = getPropertyOfType(baseType, memberEscapedName);
38618
+
38619
+ const baseClassName = typeToString(baseWithThis);
38620
+ if (prop && !baseProp && memberHasOverrideModifier) {
38621
+ if (errorNode) {
38622
+ const suggestion = getSuggestedSymbolForNonexistentClassMember(memberName, baseType); // Again, using symbol name: note that's different from `symbol.escapedName`
38549
38623
suggestion ?
38550
38624
error(
38551
- member ,
38625
+ errorNode ,
38552
38626
isJs ?
38553
38627
Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1 :
38554
38628
Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1,
38555
38629
baseClassName,
38556
38630
symbolToString(suggestion)) :
38557
38631
error(
38558
- member ,
38632
+ errorNode ,
38559
38633
isJs ?
38560
38634
Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0 :
38561
38635
Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0,
38562
38636
baseClassName);
38563
38637
}
38564
- else if (prop && baseProp?.declarations && compilerOptions.noImplicitOverride && !nodeInAmbientContext) {
38565
- const baseHasAbstract = some(baseProp.declarations, hasAbstractModifier);
38566
- if (hasOverride) {
38567
- return;
38568
- }
38638
+ return MemberOverrideStatus.HasInvalidOverride;
38639
+ }
38640
+ else if (prop && baseProp?.declarations && compilerOptions.noImplicitOverride && !nodeInAmbientContext) {
38641
+ const baseHasAbstract = some(baseProp.declarations, hasAbstractModifier);
38642
+ if (memberHasOverrideModifier) {
38643
+ return MemberOverrideStatus.Ok;
38644
+ }
38569
38645
38570
- if (!baseHasAbstract) {
38646
+ if (!baseHasAbstract) {
38647
+ if (errorNode) {
38571
38648
const diag = memberIsParameterProperty ?
38572
38649
isJs ?
38573
38650
Diagnostics.This_parameter_property_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0 :
38574
38651
Diagnostics.This_parameter_property_must_have_an_override_modifier_because_it_overrides_a_member_in_base_class_0 :
38575
38652
isJs ?
38576
38653
Diagnostics.This_member_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0 :
38577
38654
Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_a_member_in_the_base_class_0;
38578
- error(member , diag, baseClassName);
38655
+ error(errorNode , diag, baseClassName);
38579
38656
}
38580
- else if (hasAbstractModifier(member) && baseHasAbstract) {
38581
- error(member, Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, baseClassName);
38657
+ return MemberOverrideStatus.NeedsOverride;
38658
+ }
38659
+ else if (memberHasAbstractModifier && baseHasAbstract) {
38660
+ if (errorNode) {
38661
+ error(errorNode, Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, baseClassName);
38582
38662
}
38663
+ return MemberOverrideStatus.NeedsOverride;
38583
38664
}
38584
38665
}
38585
- else if (hasOverride) {
38666
+ }
38667
+ else if (memberHasOverrideModifier) {
38668
+ if (errorNode) {
38586
38669
const className = typeToString(type);
38587
38670
error(
38588
- member ,
38671
+ errorNode ,
38589
38672
isJs ?
38590
38673
Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_containing_class_0_does_not_extend_another_class :
38591
38674
Diagnostics.This_member_cannot_have_an_override_modifier_because_its_containing_class_0_does_not_extend_another_class,
38592
38675
className);
38593
38676
}
38677
+ return MemberOverrideStatus.HasInvalidOverride;
38594
38678
}
38679
+
38680
+ return MemberOverrideStatus.Ok;
38595
38681
}
38596
38682
38597
38683
function issueMemberSpecificError(node: ClassLikeDeclaration, typeWithThis: Type, baseWithThis: Type, broadDiag: DiagnosticMessage) {
@@ -38638,6 +38724,48 @@ namespace ts {
38638
38724
}
38639
38725
}
38640
38726
38727
+ /**
38728
+ * Checks a member declaration node to see if has a missing or invalid `override` modifier.
38729
+ * @param node Class-like node where the member is declared.
38730
+ * @param member Member declaration node.
38731
+ * Note: `member` can be a synthetic node without a parent.
38732
+ */
38733
+ function getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement): MemberOverrideStatus {
38734
+ if (!member.name) {
38735
+ return MemberOverrideStatus.Ok;
38736
+ }
38737
+
38738
+ const symbol = getSymbolOfNode(node);
38739
+ const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType;
38740
+ const typeWithThis = getTypeWithThisArgument(type);
38741
+ const staticType = getTypeOfSymbol(symbol) as ObjectType;
38742
+
38743
+ const baseTypeNode = getEffectiveBaseTypeNode(node);
38744
+ const baseTypes = baseTypeNode && getBaseTypes(type);
38745
+ const baseWithThis = baseTypes?.length ? getTypeWithThisArgument(first(baseTypes), type.thisType) : undefined;
38746
+ const baseStaticType = getBaseConstructorTypeOfClass(type);
38747
+
38748
+ const memberHasOverrideModifier = member.parent
38749
+ ? hasOverrideModifier(member)
38750
+ : hasSyntacticModifier(member, ModifierFlags.Override);
38751
+
38752
+ const memberName = unescapeLeadingUnderscores(getTextOfPropertyName(member.name));
38753
+
38754
+ return checkMemberForOverrideModifier(
38755
+ node,
38756
+ staticType,
38757
+ baseStaticType,
38758
+ baseWithThis,
38759
+ type,
38760
+ typeWithThis,
38761
+ memberHasOverrideModifier,
38762
+ hasAbstractModifier(member),
38763
+ isStatic(member),
38764
+ /* memberIsParameterProperty */ false,
38765
+ memberName,
38766
+ );
38767
+ }
38768
+
38641
38769
function getTargetSymbol(s: Symbol) {
38642
38770
// if symbol is instantiated its flags are not copied from the 'target'
38643
38771
// so we'll need to get back original 'target' symbol to work with correct set of flags
0 commit comments