Angular Dependency Declarations: too many ways to screw it up

Because Angular’s modules and dependencies are layered on top of the Javascript language they suffer from a fairly verbose form. There are several ways to declare a dependency and they are documented in the obvious-in-retrospect $injector documentation.  However, only one of these is acceptable in my book.  The one true way to declare an Angular provider:

angular.module("SomeModule").service("SomeService", ["Dep1", "$rootScope", "Dep2", function SomeService(Dep1, $rootScope, Dep2) {
// your code here
}]); // such an elegant way to close the declaration!

This is almost DRY.  At least the repeated declarations are close to each other and could probably be verified by a lint checker.

Why do the other forms exist at all?  The Angular devs correctly realized that for someone’s first experience with Angular the above is just too syntactically noisy. They came up with a very clever trick that works well for fiddles and plunks but breaks confusingly when used in any real production environment. The trick is to check if the dependency list passed to module.service is an array or a function. If it’s an array then it’s presumed to have a list of dependencies and a function. Oh the overloading! Why not just put the function as a 3rd parameter? If it’s just a function then Angular gets the source of the function (yes, you can call fn.toString()) and then parses out the declared argument names and treats those as dependencies to be injected.

module.controller("MyController", function ($scope) {
// some code
});

It would be great if this just worked all the time. Unfortunately minifiers will replace all argument names with single letter arguments and Angular will spit out a confusing error message:
Error: Unknown provider: aProvider at Error ()

The “aProvider” here is the function argument name “a” and “Provider” (which gives you a hint as to how Angular manages its namespace).