Indeed, it’s the factory pattern which can be used to encapsulate dependencies and break the dependency leak from the innermost object to the outermost object. It’s well explained by Zoran Horvat in C#: Cascading Abstract Factories to Eliminate Dependencies and also in his Pluralsight course Tactical Design Patterns in .NET: Creating Objects > Understanding Dependencies, Covariance, and Contravariance > Encapsulation of Dependencies.
By the way, dependency injection and factories are complementary: to create an object, instead of injecting all its dependencies, it can be more convenient to inject only its factory; all the factory dependencies can be resolved and injected automatically into the factory by the DI container.
Using TypeScript, we can move a step further with dependency inversion using interfaces and abstract factories. The overall design is more complicated so it’s a personal/team choice to make by project.
