Home > Mobile >  Generic function picking one property
Generic function picking one property

Time:11-05

I was trying to create a generic function transforming an object by filtering only one property - sth along the lines of:

function pickOnePropertyUntyped(data: any, key: any): any {
  return {
    [key]: data[key]
  }
}

Thus desired behaviour is:

const a: A = {
  a: 'a',
  b: 1
}
const r = pickOnePropertyUntyped(a, 'a'); // {'a': 'a'}

I am having problems with getting desired types and implementation.

My attempt is:

type UnionToIntersection<U> = 
    (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never 

type NoUnion<Key> =
    [Key] extends [UnionToIntersection<Key>] ? Key : never; 

type PickOneProperty<T, P extends keyof T & string> = {
  [K in keyof T as K extends NoUnion<P> ? K : never]: T[K]
}

function pickOneProperty<T, K extends keyof T & string>(
   data: T,
   key: K,
): PickOneProperty<T, K> {
  return {
    [key]: data[key]
  }
}

Which:

Unfortunately, it flags return type as incorrect.

Playground

CodePudding user response:

What you are observing here is more or less a missing feature in TypeScript. Most of the times when you are trying to use a computed property key, TypeScript automatically adds an index signature to the type of the object. There are only some exceptions; for example when the type of the key is already a known string literal type (as shown here). See discussions about this problem here.

The index signature breaks the typing here. As far as I know, there are no "satisfying" solutions to this problem. Even a normal type assertion does not work here as TypeScript sees no overlap between the two types. What you can do is to assert to unkown first and then to PickOneProperty<T, K>

function pickOneProperty<T, K extends keyof T & string>(
   data: T,
   key: K,
): PickOneProperty<T, K> {
  return {
    [key]: data[key]
  } as unknown as PickOneProperty<T, K>
}

Just be aware that there is pretty much no type-safety in this implementation now.


Playground

  • Related