microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.83k stars 12.46k forks source link

Suggestion: Interfaces as string literals #2902

Closed park9140 closed 8 years ago

park9140 commented 9 years ago

I have been looking at dependency injection and a few issues with refactoring in regards to _.pluck. One of the primary things I run into is that I want these things to enforce the interface for refactoring.

A few examples

Interface used as a symbol for dependency injection

interface IExampleInterface {
  interfaceProperty: string;
}

function angularInjection(...fields: string[]) {
  return (target: Function) {
    target.$inject = fields;
  }
}

@angularInjection(nameof(IExampleInterface));
class Controller {
  constructor(injectedExample: IExampleInterface) {
  }
}

would compile to

function angularInjection() {
    var fields = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        fields[_i - 0] = arguments[_i];
    }
    return function (target) {
        target.$inject = fields;
    };
}
var Controller = (function () {
    function Controller(injectedExample) {
    }
    Controller = __decorate([
        angularInjection('IExampleInterface')
    ], Controller);
    return Controller;
})();

In the case of an interface like pluck we could do something like this

interface IExampleInterface {
  prop: string;
}

var items: IExampleInterface[] = [{prop: 'test1'},{prop: 'test1'}];
var props: string[] = _.pluck(items, nameof(IExampleInterface.prop));

would compile to

var items = [{prop: 'test1'}, {prop: 'test1'}];
var props = _.pluck(items, 'prop');

In this way we could enforce some level of type safety in common javascript reflection use cases.

RyanCavanaugh commented 9 years ago

Are we basically talking about C#'s nameof operator?

park9140 commented 9 years ago

Hadn't used it before but seems right.

danquirk commented 9 years ago

Is this a dupe of #394 then?

park9140 commented 9 years ago

@danquirk, seems like this is different.

park9140 commented 9 years ago

@danquirk, seems to me that the initial suggestion on #394 is quite different. This nameof methodology is primarily aimed at elmination of magic strings that cannot be refactored. Specifically the pluck use case isn't solved by checking the literal argument. The proposal in #394 is dangerous as noted in some of the comments and would either need runtime gating or to prevent all non literal strings from being parameters to a method describe with the memberof methodology. It also does not handle using Interface names for injection purposes as the nameof methodology would.

alex1kirch commented 9 years ago

I fully agree with @park9140. Also, the nameof methodology can be used when working with Object.observe() method.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe

// A user model
var user = {
  id: 0,
  name: 'Brendan Eich',
  title: 'Mr.'
};

// Create a greeting for the user
function updateGreeting() {
  user.greeting = 'Hello, ' + user.title + ' ' + user.name + '!';
}
updateGreeting();

Object.observe(user, function(changes) {
  changes.forEach(function(change) {
    // Any time name or title change, update the greeting
    if (change.name === nameof(user.name) || change.name === nameof(user.title)) {
      updateGreeting();
    }
  });
});
mhegazy commented 8 years ago

looks like another duplicate of #3628