Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript dynamic object return type based on array parameter

With the following abstraction, I am able to call this method and the return type is based on the string based fields I pass into it shown below.

abstract class Entity {
  abstract id: string
}

class User extends Entity {
  constructor(obj: Partial<User>) {
    super()
    Object.assign(this, obj)
  }

  id: string
  name: string
  age: number
}

class Post extends Entity {
  constructor(obj: Partial<Post>) {
    super()
    Object.assign(this, obj)
  }

  id: string
  content: string
  time: Date
}


export const useSyncFields = <T extends Entity, U extends (keyof T & string)[]>(
  type: { new(obj: T): T },
  id: string,
  fields: U,
): { [K in U[number]]: T[K] } => {
   ...
}

const user1: {id: string, name: string} = useSyncFields(User, "1234", ["id", "name"])
const user2: {id: string, age: number} = useSyncFields(User, "1234", ["id", "age"])
const post: {content: string} = useSyncField(Post, "4567", ["content"]
// etc

By using the type params <T extends Entity, U extends (keyof T & string)[] and the return type { [K in U[number]]: T[K] }, the inferred return type from Typescript "correct", but neither VSCode or WebStorm do an awesome job inferring the type correctly.

I feel like there’s a better set of type params / return types that would be better inferred. Is what I have really the best I could be doing here? Or is there some type ninja out there who can guide me to a better solution?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

If you simply want to make this simpler, I’d go with:

export const useSyncFields = <T extends Entity, U extends keyof T>(
  type: { new(obj: T): T },
  id: string,
  fields: U[],
): Pick<T, U> => {
   return {} as any // implementation
}

If you don’t care about the length or order of arrays in your type then you don’t need be generic over the whole array. So this is only generic over the members of the fields array. This means you don’t have to go fishing with U[number] to get the union of keys.

It also uses the Pick utility type which saves you from having the complex looking mapped return type.

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading