CRice
CRice

Reputation: 32176

Filter keys of a type using another type

I'm trying to create a type that will recursively exclude keys from one type if they do not appear in another. For example, given the two types:

type TargetType = {a: true, b: {c: true, d: true}};
type InputType = {a: string, b: { c: boolean, d: number, e: string }, f: number};

I want to create some generic type PickMatchedKeys<T, U> such that

PickMatchedKeys<InputType, TargetType> = {a: string, b: { c: boolean, d: number }}

Anyone have an idea of how to accomplish this? Here is my best attempt so far:

type PickMatchedKeys<T, U> = {
    [K in keyof T]: K extends keyof U ? PickMatchedKeys<T[K], U[K]> : never;
}

This works alright, but the problem is that the keys f and b.e still exist in PickMatchedKeys<InputType, TargetType>, but have type never. Ideally those keys would not be present in the final type at all.

Upvotes: 2

Views: 692

Answers (1)

jcalz
jcalz

Reputation: 328186

Why not something like this:

type PickMatchedKeys<T, U> = {
  [K in (keyof T) & (keyof U)]: 
    T[K] extends object ? PickMatchedKeys<T[K], U[K]> : T[K];
}

You don't really need conditional types to restrict the keys to those which are common to both T and U, since you can just intersect the key types. The only thing you need to do is make sure not to drill down into primitives; that's where the T[K] extends object part comes in. You should decide if you want some other criterion (e.g., do something special with arrays) but that's the basic plan.

Let's examine it:

type TestIt = PickMatchedKeys<InputType, TargetType>;
declare const z: TestIt;
z.a // string
z.b.c // boolean
z.b.d // number
z.b.e // error
z.f // error

Looks good to me. Hope that helps; good luck!

Upvotes: 4

Related Questions