1

i try to create correct typings for situations, when trying to map the array of objects to array with the same objects but using the property value as index key..

Code on playground

interface ValueDefinition {
    name: string;
}

function getByName<V extends ValueDefinition>(valuDefinitions: V[]) {
    let out  = {}

    for (const key in valuDefinitions) {
        out[valuDefinitions[key].name] = valuDefinitions[key];
    }
    return out;
}

const obj: ValueDefinition = {
    name: 'super'
};


const objKey = getByName([
    obj
]);

key.super; // Typings for this output

I looking for something like this:

type WithKey<D extends ValueDefinition> = {
    [key: D['name']]: D;
}

Thanks.

2
  • Are the names known at compile time? Commented Feb 3, 2018 at 21:03
  • Yes the are known. Commented Feb 3, 2018 at 21:18

1 Answer 1

4

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!

Sign up to request clarification or add additional context in comments.

3 Comments

You beat me to it :)
@TitianCernicova-Dragomir Uh oh, sorry!
It's fine, it was fair and square :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.