Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 680c033

Browse files
authored
fix(eslint-plugin): [use-lifecycle-interface] do not report if the method uses override (#2463)
1 parent 62b3a4f commit 680c033

File tree

3 files changed

+265
-6
lines changed

3 files changed

+265
-6
lines changed

packages/eslint-plugin/docs/rules/use-lifecycle-interface.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,46 @@ class Test extends Component {
199199
}
200200
```
201201

202+
<br>
203+
204+
---
205+
206+
<br>
207+
208+
#### Default Config
209+
210+
```json
211+
{
212+
"rules": {
213+
"@angular-eslint/use-lifecycle-interface": [
214+
"error"
215+
]
216+
}
217+
}
218+
```
219+
220+
<br>
221+
222+
#### ❌ Invalid Code
223+
224+
```ts
225+
@Directive()
226+
class FoobarBase implements OnDestroy {
227+
ngOnDestroy(): void {
228+
/* some base logic here */
229+
}
230+
}
231+
232+
@Component()
233+
class FoobarComponent extends FoobarBase {
234+
ngOnDestroy(): void {
235+
~~~~~~~~~~~
236+
super.ngOnDestroy();
237+
/* some concrete logic here */
238+
}
239+
}
240+
```
241+
202242
</details>
203243

204244
<br>
@@ -356,6 +396,122 @@ class Test extends Component implements ng.OnInit, ng.OnDestroy {
356396
class Test {}
357397
```
358398

399+
<br>
400+
401+
---
402+
403+
<br>
404+
405+
#### Default Config
406+
407+
```json
408+
{
409+
"rules": {
410+
"@angular-eslint/use-lifecycle-interface": [
411+
"error"
412+
]
413+
}
414+
}
415+
```
416+
417+
<br>
418+
419+
#### ✅ Valid Code
420+
421+
```ts
422+
@Directive()
423+
class FoobarBase implements OnDestroy {
424+
ngOnDestroy(): void {
425+
/* some base logic here */
426+
}
427+
}
428+
429+
@Component()
430+
class FoobarComponent extends FoobarBase {
431+
override ngOnDestroy(): void {
432+
super.ngOnDestroy();
433+
/* some concrete logic here */
434+
}
435+
}
436+
```
437+
438+
<br>
439+
440+
---
441+
442+
<br>
443+
444+
#### Default Config
445+
446+
```json
447+
{
448+
"rules": {
449+
"@angular-eslint/use-lifecycle-interface": [
450+
"error"
451+
]
452+
}
453+
}
454+
```
455+
456+
<br>
457+
458+
#### ✅ Valid Code
459+
460+
```ts
461+
class BaseClass {
462+
ngOnInit(): void {
463+
/* base initialization */
464+
}
465+
}
466+
467+
@Component()
468+
class DerivedComponent extends BaseClass {
469+
override ngOnInit(): void {
470+
super.ngOnInit();
471+
/* derived initialization */
472+
}
473+
}
474+
```
475+
476+
<br>
477+
478+
---
479+
480+
<br>
481+
482+
#### Default Config
483+
484+
```json
485+
{
486+
"rules": {
487+
"@angular-eslint/use-lifecycle-interface": [
488+
"error"
489+
]
490+
}
491+
}
492+
```
493+
494+
<br>
495+
496+
#### ✅ Valid Code
497+
498+
```ts
499+
@Directive()
500+
class BaseDirective implements OnInit {
501+
ngOnInit(): void {
502+
/* base initialization */
503+
}
504+
}
505+
506+
@Component()
507+
class DerivedComponent extends BaseDirective {
508+
override ngOnInit(): void {
509+
super.ngOnInit();
510+
/* derived initialization */
511+
}
512+
}
513+
```
514+
359515
</details>
360516

361517
<br>

packages/eslint-plugin/src/rules/use-lifecycle-interface.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,23 @@ export default createESLintRule<Options, MessageIds>({
3232
]);
3333

3434
return {
35-
[`MethodDefinition[key.name=${angularLifecycleMethodsPattern}]`]({
36-
key,
37-
parent: { parent },
38-
}: TSESTree.MethodDefinition & { parent: TSESTree.ClassBody } & {
39-
parent: TSESTree.ClassDeclaration;
40-
}) {
35+
[`MethodDefinition[key.name=${angularLifecycleMethodsPattern}]`](
36+
node: TSESTree.MethodDefinition & { parent: TSESTree.ClassBody } & {
37+
parent: TSESTree.ClassDeclaration;
38+
},
39+
) {
40+
const {
41+
key,
42+
parent: { parent },
43+
} = node;
44+
4145
if (!ASTUtils.getAngularClassDecorator(parent)) return;
4246

47+
// Do not report the method if it has the override keyword because it implies the base class is responsible for the implementation
48+
if (node.override) {
49+
return;
50+
}
51+
4352
const declaredLifecycleInterfaces =
4453
ASTUtils.getDeclaredAngularLifecycleInterfaces(parent);
4554
const methodName = (key as TSESTree.Identifier)

packages/eslint-plugin/tests/rules/use-lifecycle-interface/cases.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,56 @@ export const valid: readonly (string | ValidTestCase<Options>)[] = [
4444
}
4545
`,
4646
'class Test {}',
47+
// Test case for override keyword - base class with interface
48+
`
49+
@Directive()
50+
class FoobarBase implements OnDestroy {
51+
ngOnDestroy(): void {
52+
/* some base logic here */
53+
}
54+
}
55+
56+
@Component()
57+
class FoobarComponent extends FoobarBase {
58+
override ngOnDestroy(): void {
59+
super.ngOnDestroy();
60+
/* some concrete logic here */
61+
}
62+
}
63+
`,
64+
// Test case for override keyword - non-Angular base class
65+
`
66+
class BaseClass {
67+
ngOnInit(): void {
68+
/* base initialization */
69+
}
70+
}
71+
72+
@Component()
73+
class DerivedComponent extends BaseClass {
74+
override ngOnInit(): void {
75+
super.ngOnInit();
76+
/* derived initialization */
77+
}
78+
}
79+
`,
80+
// Test case for override keyword - Angular base class without interface
81+
`
82+
@Directive()
83+
class BaseDirective implements OnInit {
84+
ngOnInit(): void {
85+
/* base initialization */
86+
}
87+
}
88+
89+
@Component()
90+
class DerivedComponent extends BaseDirective {
91+
override ngOnInit(): void {
92+
super.ngOnInit();
93+
/* derived initialization */
94+
}
95+
}
96+
`,
4797
];
4898

4999
export const invalid: readonly InvalidTestCase<MessageIds, Options>[] = [
@@ -248,4 +298,48 @@ export const invalid: readonly InvalidTestCase<MessageIds, Options>[] = [
248298
}
249299
`,
250300
}),
301+
convertAnnotatedSourceToFailureCase({
302+
description:
303+
'it should fail if lifecycle method is declared without implementing its interface even when extending a base class (no override keyword)',
304+
annotatedSource: `
305+
@Directive()
306+
class FoobarBase implements OnDestroy {
307+
ngOnDestroy(): void {
308+
/* some base logic here */
309+
}
310+
}
311+
312+
@Component()
313+
class FoobarComponent extends FoobarBase {
314+
ngOnDestroy(): void {
315+
~~~~~~~~~~~
316+
super.ngOnDestroy();
317+
/* some concrete logic here */
318+
}
319+
}
320+
`,
321+
messageId,
322+
data: {
323+
interfaceName: ASTUtils.AngularLifecycleInterfaces.OnDestroy,
324+
methodName: ASTUtils.AngularLifecycleMethods.ngOnDestroy,
325+
},
326+
annotatedOutput: `import { OnDestroy } from '@angular/core';
327+
328+
@Directive()
329+
class FoobarBase implements OnDestroy {
330+
ngOnDestroy(): void {
331+
/* some base logic here */
332+
}
333+
}
334+
335+
@Component()
336+
class FoobarComponent extends FoobarBase implements OnDestroy {
337+
ngOnDestroy(): void {
338+
339+
super.ngOnDestroy();
340+
/* some concrete logic here */
341+
}
342+
}
343+
`,
344+
}),
251345
];

0 commit comments

Comments
 (0)