Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object with generic keys in typescript

Tags:

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?

like image 651
dieTrying Avatar asked Dec 31 '19 13:12

dieTrying


People also ask

What is generic in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.

Is there generics in TypeScript?

TypeScript also supports generic classes. The generic type parameter is specified in angle brackets (<>) following the name of the class. A generic class can have generic fields or methods.

How do you pass a generic type as parameter TypeScript?

Assigning Generic ParametersBy passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.


1 Answers

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

A smaller & more robust version would use reduce. 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.keys(obj) as Array<keyof T>).reduce((result, key) => {
      result[key] = () => obj[key];
      return result;
  }, {} as Result<T>)
}

const a = transform(hash)

a.first() // returns "string"
a.second() // return "number"
like image 89
felixmosh Avatar answered Sep 23 '22 05:09

felixmosh