All topics
Servicesintermediate

The inject() Function

Explain how the inject() function provides an alternative to constructor injection and where it's required rather than optional.

The inject() function lets you retrieve a dependency from Angular's DI system without declaring it as a constructor parameter — it's called directly inside a field initializer, a factory function, a functional guard, or anywhere else that executes within what Angular calls an "injection context." It was introduced primarily to support functional APIs (guards, resolvers, interceptors) that are plain functions with no constructor to attach parameters to, but it's since become popular as a general alternative to constructor injection even inside classes.

Constructor injection is like having ingredients delivered to your kitchen counter before you start cooking; inject() is like reaching into the pantry yourself mid-recipe — both get you the ingredient, but reaching into the pantry only works while you're still standing in the kitchen (the injection context), not after you've already left for the day.

Key Concepts

1
The core rule interviewers test is: inject() only works inside an injection context, which includes a constructor body, a field initializer of a class being constructed by DI, or the synchronous body of a function explicitly run within an injection context (like a functional route guard or interceptor at the moment Angular invokes it). Calling inject() inside a setTimeout callback or after an await — anywhere execution has left the synchronous injection context — throws a runtime error.
inject()setTimeoutawait
2
Compared to constructor injection, inject() as a field initializer has a genuine ergonomic advantage in classes with many dependencies or with inheritance: it avoids the "constructor parameter explosion" problem and, notably, avoids having to repeat every parent class's constructor dependencies in a subclass's constructor just to call super(), since each class can independently call inject() for what it needs.
inject()super()
3
A fair interview answer acknowledges this is partly a style preference for simple cases — constructor injection remains perfectly valid and arguably more discoverable for some teams — but inject() is effectively mandatory for functional guards, resolvers, and interceptors, since those are plain functions with no constructor at all.
inject()