to-signal-in-injection-context

May 5, 2026 · View on GitHub

Checks that toSignal() is called inside an injection context, or that an explicit Injector is provided in the second argument, or that manualCleanup is enabled, to avoid the NG0203 runtime error.

Documentation

Configuration

  • in the recommended preset (see the README for the configuration)
  • or just this rule:
{
  "rules": {
    "angular-eslint-injection-context/to-signal-in-injection-context": "error"
  }
}

❌ Invalid

All the invalid cases are without an injector or manual cleanup. See the valid cases below to see an example of how to provide an explicit Injector or enable manual cleanup.

  • in lifecycle methods, notably ngOnInit
@Component()
export class ProductPage implements OnInit {
  ngOnInit(): void {
    toSignal(someObservable);
  }
}
  • in any methods other than the constructor
@Component({
  template: `<form (submit)="save()"></form>`
})
export class ProductEditPage {
  save(): void {
    toSignal(someObservable);
  }
}
  • in callbacks
@Component()
export class ProductPage implements OnInit {
  ngOnInit(): void {
    somePromise().then(() => {
      toSignal(someObservable);
    }).catch(() => {});  
  }
}

Note

The rule reports both on asynchronous and synchronous callbacks, see the known limitation documentation.

  • after awaiting (which is equivalent to be in a .then() callback)
@Component()
export class ProductEditPage {
  async save(): Promise<void> {
    await somePromise();
    toSignal(someObservable);
  }
}
  • in non-Angular classes
export class Product {
  constructor() {
    toSignal(someObservable);
  }
}
  • in standalone functions
function someFunction(): void {
  toSignal(someObservable);
} 

✅ Valid

  • in constructors of components, directives, pipes and injectables/services
@Component()
export class ProductsPage {
  constructor() {
    toSignal(someObservable);
  }
}
  • in property initializers of components, directives, pipes and injectables/services
@Component()
export class ProductPage {
  private readonly someSignal = toSignal(someObservable);
}
  • when providing an explicit Injector
@Component()
export class ProductPage implements OnInit {
  private readonly injector = inject(Injector);

  ngOnInit(): void {
    toSignal(someObservable, {
      injector: this.injector,
    });
  }
}

Note

Prefer a literal object as in this example. If the second argument is a variable, the lint rule will not check if injector is actually present, see the known limitation documentation.

  • when enabling manual cleanup (be careful with this option)
@Component()
export class ProductPage implements OnInit {
  ngOnInit(): void {
    toSignal(someObservable, {
      manualCleanup: true,
    });
  }
}
  • in explicit injection context
@Injectable({ providedIn: 'root' })
export class MyService {
  private readonly environmentInjector = inject(EnvironmentInjector);

  someMethod() {
    runInInjectionContext(this.environmentInjector, () => {
      toSignal(someObservable);
    });
  }
}

Note

The rule only detects runInInjectionContext() or TestBed.runInInjectionContext() in the current function, see the known limitation documentation.

  • when asserted
function customOperator(injector: Injector) {
  if (!injector) {
    assertInInjectionContext(customOperator);
  }
  toSignal(someObservable);
}

Back to README