Tarık İNCE
Tarık İNCE

Reputation: 25

Is it possible to extract keys from a parameter for type inference in Typescript?

I was trying to extract some properties from an object.
The keys which should be extracted are given as parameter to the function.

For example, for this code, it copies the specific keys from the object and returns new, another object with the same keys.

function pick(source: object, ...keys: string[]) {
    const returnValue = {} 
    for (const key in source) {
        const index = keys.indexOf(key)
        if (index < 0) continue
        keys.splice(index, 1)
        returnValue[ key ] = source[ key ]
    }
    return returnValue
}

When I use this function, TypeScript infers the return type as {} which is an empty object. Is there a way I can get this as { [key: .. in parameter 'keys' ..]: any }?

I tried this but it did not work.

function pick<O>(source: object, ...keys: (keyof O)[]): O {
    const returnValue: O = {} 
    for (const key in source) {
        const index = keys.indexOf(key)
        if (index < 0) continue
        keys.splice(index, 1)
        returnValue[ key ] = source[ key ]
    }
    return returnValue
}

Thank You

Upvotes: 2

Views: 16728

Answers (3)

andrea06590
andrea06590

Reputation: 1299

If you want to "parse" two objects using same key, you should use Object.keys

let's assume you have two objects myObject and myOtherObj, if the keys are the same just use only one mapper :

Object.keys(myObject).map((key) => {
   console.log(key) // should be the same for both obj
   // your two obj values
   console.log(myObject[key]);
   console.log(myOtherObj[key]);
});

EDIT : you can also do Object.keys(myOtherObj).map....

Upvotes: 0

artem
artem

Reputation: 51629

There is built-in Pick type that was added in TypeScript 2.1 exactly for describing such a function:

function pick<T, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> {

Just for reference, it's a very simple mapped type defined as

type Pick<T, K extends keyof T> = {[k in K]: T[k]};

However, your implementation does not compile without type casts because TypeScript complains about keys.indexOf(key) - key is now keyof T and it's not compatible with the type of indexOf parameter, which is keys element type - K.

So I made slightly different implementation with a loop that goes over keys array. I do not remove "used" keys from keys - if there are duplicates (which is not likely I suppose), assigning the same value twice will do no harm here:

function pick<T, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> {
    const returnValue = {} as Pick<T, K>;
    keys.forEach(k => {
        returnValue[k] = source[k];
    });
    return returnValue;
}

Upvotes: 2

mickaelw
mickaelw

Reputation: 1513

I think you can do a simple mapper between your objects:

interface OriginalObject {
  property1: string
  property2: string
  property3: string
}

interface MappedObject {
   mappedProperty1: string,
   mappedProperty2: string
}

class Mapper {
   static mapOriginalObjectToMappedObject(object: OrginalObject): MappedObject {
      return {
         mappedProperty1: object.property2, 
         mappedProperty2: object.property3
      }
   }

   static mapFooToBaz ...
   static mapBarToFoo ...
}

In your code:

const objectA: OriginalObject = {
  property1: "foo",
  property2: "bar",
  property3: "baz"
}
const mappedObjectA = Mapper.mapOriginalObjectToMappedObject(objectA)

With this way, your code stay simple

Upvotes: 0

Related Questions