4

I want to have a method that accepts an array of objects and an array of some of the objects keys. The method will return an array of arrays of object values but only of the selected keys.

data:

[
  {"firstName": "Jane", "lastName": "Doe"},
  {"firstName": "John", "lastName": "Doe"}
]

fields:

["firstName"]

result:

[["Jane"], ["John"]]

By now I have a function that provides desired outcome but I am not sure how to handle the types better.

mapToCsvData: (data: { [key: string]: any }[], fields: string[]) => {
  return data.map((item: any) => {
    return fields.map(field => item[field]);
  });
}

I have tried some variations of the next snippet but I get an error.

mapToCsvData: <T extends object>(data: T[], fields: keyof T[]) => {
Property 'map' does not exist on type 'number'.
CrossTheDev
  • 151
  • 1
  • 11

1 Answers1

5

You will need an extra type parameter to capture the actual tuple of keys being passed in. You can them map this tuple to the corresponding property types in T. It all works out very nicely:

type MapKeyTupleToProps<T, P extends [keyof T] | Array<keyof T>> = {
    [K in keyof P]: P[K] extends keyof T ? T[P[K]] : never
}
const m = {
    mapToCsvData: <T, P extends [keyof T] | Array<keyof T>>(data: T[], fields: P): MapKeyTupleToProps<T, P> => {
        return data.map((item: any) => {
            return fields.map(field => item[field]);
        }) as any;
    }
}

const data = [
    {"firstName": "Jane", "lastName": "Doe", age: 0},
    {"firstName": "John", "lastName": "Doe", age: 0}
]

let r = m.mapToCsvData(data, ["firstName", "lastName"]) // [string, string]
let r2 = m.mapToCsvData(data, ["firstName", "age"]) //[string, number]
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • Thank you. This is really weird but when I move the MapKeyTupleToProps to my main file with types, the P in MapKeyTupleToProps lights up with an error "Type '[keyof T]' is not assignable to type 'flat'." – CrossTheDev May 28 '19 at 13:15
  • @CrossTheDev not sure why that happens .. it should work .. this type of mapped tuples is supported from 3.1 I believe. – Titian Cernicova-Dragomir May 28 '19 at 13:17
  • I am currently on 3.4.1 so that should not be a problem. – CrossTheDev May 28 '19 at 13:20
  • I figured out the probem behind error messages for t "P". Turns out that my linter prefers [] instead of Array so I changed "Array>" to "keyof T[]" but the correct variation is "(keyof T)[]". My bad. – CrossTheDev May 29 '19 at 06:08
  • @CrossTheDev yeah .. I prefer `Array` if the item type is anything more than a single word, it is easier to read. For`(keyof T)[]` you need to put `()` and you can easily miss the `[]` at the end. I believe that lint rule is configurable though – Titian Cernicova-Dragomir May 29 '19 at 06:32