javascripttypescripttypescript-typingstypescript-generics

Object with generic keys in typescript


I'd like to create a common function which will take an object, then do some transformations and return new object with same keys and different values. I'm trying to make it "strongly-typed", so everyone who uses it will have benefits of TS and non-existing keys should throw an error.

What I have for now:

const hash = {
  "first": 1,
  "second": 2,
  "third": 3,
}

type Mapper<T> = {
  [key in keyof T]: number
}

type Result<T>= {
  [key in keyof T]: () => number
}

const transform = <T>(mapper: Mapper<T>) => {
  const result = {} as Result<T>

  (Object.keys(mapper) as (keyof T)[]).map(key => {
    result[key] = () => mapper[key]
  })

  return result
}

type Hash = typeof hash

const a = transform<Hash>(hash)

a.first()
// a.fifth() OK error

It works well, but I'm looking for solutions to solve this:

  1. Remove type assertion const result = {} as Result<T>

  2. Remove type assertion (Object.keys(mapper) as (keyof T)[]) (or use Object.entries, but seems it also requires type assertion in this case)

Could I implement the same, but in more "clean" way in Typescript?


Solution

  • Object.keys returns always string[] therefore you will need the casting.

    A smaller & more robust version would use Object.fromEntries. Another small improvement would be to use the type of the original key, with T[Key].

    const hash = {
      "first": "someString",
      "second": 2,
      "third": 3,
    };
    
    type Result<T>= {
      [Key in keyof T]: () => T[Key]
    };
    
    const transform = <T extends object>(obj: T): Result<T> => {
      return Object.fromEntries(
        Object.keys(obj).map(
            (key) => [key, () => obj[key as keyof T]]
        )
      ) as Result<T>;
    }
    
    const a = transform(hash);
    
    const first = a.first(); // returns "string"
    //    ^? const first: string
    const second = a.second(); // return "number"
    //    ^? const second: number
    

    Playground link