Issue 1:
When mapping a object, TS can't retain the keys accessor.

Following example uses lodash _.mapValues, which maps a object.

var newUsers = _.mapValues({
'fred': { 'user': 'fred', 'age': 40 },
'pebbles': { 'user': 'pebbles', 'age': 1 }
}, function(o) { return o.age; }); // -> _.Dictionary<number>
let ageFred = newUsers['fred']; // is number
newUsers.fred // <-- invalid because TypeScript can't retain the keys.

I can't think a way to type _.mapValues so it would retain the keys. Retaining keys requires new kind of typing capabilities for the TypeScript.

I find myself needing to retain keys in surprising many places, e.g. when uncurrying a object full of functions.

Issue 2:
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 ?