Related to this documentation in Angular website:

https://angular.dev/guide/components/inheritance

Simplifying inheritance by removing the need for:

  • having a constructor in the parent just for common dependency injection
  • having a constructor in the child just for common dependency injection
  • calling super for common dependency injection
  • calling super for common lifecycle hooks

Stackblitz link:

https://stackblitz.com/edit/stackblitz-starters-tzhggu9s?file=src%2Fmain.ts

Parent component:

@Component({ template: `` })
abstract class Parent {
  protected heroService = inject(HeroService);

  ngOnInit() {
    this.onInit();
  }

  protected abstract onInit(): void;
}

Child component:

@Component({
  selector: 'child',
  template: `Child`,
})
class Child extends Parent {
  override onInit() {
    this.heroService.log();
  }
}

App:

@Component({
  selector: 'app-root',
  template: ``,
  imports: [Child],
})
class App {}

bootstrapApplication(App);

Common service:

@Injectable({ providedIn: 'root' })
class HeroService {
  log() {
    console.log('heroService');
  }
}

Advice is to have:

{
  "compilerOptions": {
    "noImplicitOverride": true,
  }
}

in tsconfig if not already there.
Goal is to avoid dev to mistakenly write an ngOnInit without knowing they'd be overriding the parent one.