8

The Pick type is included with TypeScript. It's implementation is as follows:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

How would you write a PickByValue type such that the following works:

type Test = {
  includeMe: 'a' as 'a',
  andMe: 'a' as 'a',
  butNotMe: 'b' as 'b',
  orMe: 'b' as 'b'
};

type IncludedKeys = keyof PickByValue<Test, 'a'>;
// IncludedKeys = 'includeMe' | 'andMe'

2 Answers2

12

Assuming you intend Test to be this:

type Test = {
  includeMe: 'a',
  andMe: 'a',
  butNotMe: 'b',
  orMe: 'b'
};

and assuming that you want PickByValue<T, V> to give all the properties which are subtypes of V (so that PickByValue<T, unknown> should be T), then you can define PickByValue like this:

type PickByValue<T, V> = Pick<T, { [K in keyof T]: T[K] extends V ? K : never }[keyof T]>
type TestA = PickByValue<Test, 'a'>; // {includeMe: "a"; andMe: "a"}
type IncludedKeys = keyof PickByValue<Test, 'a'>; // "includeMe" | "andMe"

But if all you want is IncludedKeys, then you can do that more directly with KeysMatching<T, V>:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];
type IncludedKeysDirect = KeysMatching<Test, 'a'> // "includeMe" | "andMe"

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • After reflecting on this I realized `KeysMatching` was a little more flexible for my specific use case. I came close with my initial tries but wasn't quite getting there. Thanks so much! – Braden Snell Mar 15 '19 at 10:09
  • very nice answer `type IntermediateType { includeMe: includeMe", andMe: "andMe, butNotMe: never, orMe: never }` and `type Result = IntermediateType[string]` produces `"includeMe" | "andMe" | never | never`, but `never` is stripped out, so it results in `"includeMe" | "andMe"` – bristweb Dec 05 '22 at 21:35
0

As of TypeScript 4.1, you can use an as clause to simplify the mapping:

type Test = {
  includeMe: 'a',
  andMe: 'a',
  butNotMe: 'b',
  orMe: 'b'
};

type PickByValue<T, V> = { [K in keyof T as T[K] extends V ? K : never]: T[K] };

type IncludedKeys = keyof PickByValue<Test, 'a'>;
// IncludedKeys = 'includeMe' | 'andMe'

Link to playground

Nate Whittaker
  • 1,866
  • 15
  • 14