You should make your ValueDefinition interface generic in the string literal type of the name property, so you can extract it later:
interface ValueDefinition<K extends string = string> {
name: K
}
Then you can represent the output of your getByName() function as the following mapped type:
type ValueDefinitionObject<K extends string> = {[P in K]: ValueDefinition<P>}
And revise the signature of getByName to be generic in the set of name literals:
function getByName<K extends string>(valuDefinitions: Array<ValueDefinition<K>>): ValueDefinitionObject<K> {
let out = {} as ValueDefinitionObject<K>
for (const key in valuDefinitions) {
out[valuDefinitions[key].name] = valuDefinitions[key];
}
return out;
}
At this point it will work, but you have to be careful to declare your obj so that the type of the value 'super' is inferred as 'super' and not string. This identity function will help:
function asValueDefinition<K extends string>(vd: ValueDefinition<K>): ValueDefinition<K> {
return vd;
}
Okay, let's try it (with another one I'm adding):
const obj = asValueDefinition({ name: 'super' });
const obj2 = asValueDefinition({ name: 'thing' });
If you inspect them they show up as ValueDefinition<'super'> and ValueDefinition<'thing'>.
const objKey = getByName([obj, obj2]);
And objKey is typed as ValueDefinitionObject<'super'|'thing'>. Let's use it:
objKey.super; // okay, ValueDefinition<'super'>
objKey.thing; // okay, ValueDefinition<'thing'>
objKey.nope; // error, property 'nope' doesn't exist
Does that work for you? Good luck!