<!DOCTYPE html>
<html>
  <body>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/reflect-metadata/0.1.8/Reflect.min.js"></script>
    <script src="script.js"></script>
    <script>
      var Account = new AccountController();
      Account.Login({ username: "user", password: "111"});
    </script>
   </body>
</html>
var ControllerNameMetadataKey = "Habr_PFight77_ControllerName";
function Controller(name: string) {
	return (target: Function) => {
    Reflect.defineMetadata(ControllerNameMetadataKey, name, target.prototype);
  };
}
function Action(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> {  
  // Запоминаем исходную функцию
  var originalMethod = descriptor.value;
  // Подменяем ее на нашу обертку
  descriptor.value = function ActionWrapper () {
  		// Получаем url, сохраненное декоратором Controller 
      var controllerName = Reflect.getMetadata(ControllerNameMetadataKey, target);
      var url = "/" + controllerName + "/" + propertyKey;
  		// Передаем url последним параметром
  		[].push.call(arguments, url);
      // Вызываем исходный метод
      originalMethod.apply(this, arguments);
  };
  // Обновляем дескриптор
  return descriptor;
}
function post(data: any, args: IArguments): any {
	var url = args[args.length - 1];
  document.body.innerHTML = "Performing request to url: " + url + 
  	" <br>Data: " + JSON.stringify(data);
  return $.ajax({ url: url, data: data, method: "POST" });
}

@Controller("Account")
class AccountController {
	@Action
  public Login(data: any): any {
    return post(data, arguments);
  }
}

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}