Issue 1:
Motivations

A lot of JavaScript library/framework/pattern involve computation based on the property name of an object. For example Backbone model, functional transformation pluck, ImmutableJS are all based on such mechanism.

//backbone
var Contact = Backbone.Model.extend({})
var contact = new Contact();
contact.get('name');
contact.set('age', 21);

// ImmutableJS
var map = Immutable.Map({ name: 'François', age: 20 });
map = map.set('age', 21);
map.get('age'); // 21

//pluck
var arr = [{ name: 'François' }, { name: 'Fabien' }];
_.pluck(arr, 'name') // ['François', 'Fabien'];

We can easily understand in those examples the relation between the api and the underlying type constraint.

In the case of the backbone model, it is just a kind of proxy for an object of type :

interface Contact {
name: string;
age: number;
}

For the case of pluck, it's a transformation

T[] => U[]

where U is the type of a property of T prop.

However we have no way to express such relation in TypeScript, and ends up with dynamic type.

Proposed solution

The proposed solution is to introduce a new syntax for type T[prop] where prop is an argument of the function using such type as return value or type parameter.

With this new type syntax we could write the following definition :

declare module Backbone {

class Model<T> {
get(prop: string): T[prop];
set(prop: string, value: T[prop]): void;
}
}

declare module ImmutableJS {
class Map<T> {
get(prop: string): T[prop];
set(prop: string, value: T[prop]): Map<T>;
}
}

declare function pluck<T>(arr: T[], prop: string): Array<T[prop]> // or T[prop][]

This way, when we use our Backbone model, TypeScript could correctly type-check the get and set call.

interface Contact {
name: string;
age: number;
}
var contact: Backbone.Model<Contact>;

var age = contact.get('age');
contact.set('name', 3) /// error

The prop constant

Constraint

Obviously the constant must be of a type that can be used as index type (string, number, Symbol).

Case of indexable

Let's give a look at our Map definition:

declare module ImmutableJS {
class Map<T> {
get(prop: string): T[string];
set(prop: string, value: T[string]): Map<T>;
}
}

If T is indexable, our map inherit of this behavior:

var map = new ImmutableJS.Map<{ [index: string]: number}>;

Now get has for type get(prop: string): number.

Interrogation

Now There is some cases where I have pain to think of a correct behavior, let's start again with our Map definition.

If instead of passing { [index: string]: number } as type parameter we would have given

{ [index: number]: number } should the compiler raise an error ?

if we use pluck with a dynamic expression for prop instead of a constant :

var contactArray: Contact[] = []
function pluckContactArray(prop: string) {
return _.pluck(myArray, prop);
}

or with a constant that is not a property of the type passed as parameter.

should the call to pluck raise an error since the compiler cannot infer the type T[prop], shoud T[prop] be resolved to {} or any, if so should the compiler with --noImplicitAny raise an error ?

Issue 2:
Often a method will take as a string the name of a property of some object that it's working on. A common example would be http://backbonejs.org/#Model-get

It would be ideal if TypeScript could provide compile-time checking on these calls to make sure that the string is actually a valid property of the underlying model. As a possible proposal:

interface Person {
firstName: string
lastName: string
phoneNumber: string
}

function get(property: memberof Person) {}

get('firstName') // compiles
get('middleName') // would not compile

In the context of the get function, "memberof Person" would be equivalent to "string".