Benjamin M
Benjamin M

Reputation: 24527

TypeScript Generics with recursion, conditionals and matching Keys

I have the following type with 2 generic parameters:

type Result<INPUT, SPEC> = ...

The concrete type of Result depends on the various combinations of INPUT and SPEC.

How it should work:

if(INPUT extends {[key: string]: any}) {
  if(SPEC extends {[key: string]: any}) {
    Result = {[key in keyof INPUT]: SPEC[key] !== undefined
            ? Result<INPUT[key], SPEC[key]>
            : true
    }
  } else {
    // ...
  }
} else {
  // ...
}

In words:

Additional Information:

Is this somehow possible with TypeScript?

Upvotes: 1

Views: 45

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249556

Based on your description, this type should do the trick:

export type Result<INPUT, SPEC> = INPUT extends object ? SPEC extends object ? {
    [K in keyof INPUT]: SPEC extends Record<K, infer SPECK> ?
        Result<INPUT[K], SPECK> :
        true
} : never : never;

// r is { foo: true; bar: never; goo: { a: never; b: true; }; }
type r = Result<{
    foo: number,
    bar: string,
    goo: { a: number; b: string }
}, { bar: number,  goo: { a: number }}>

The part you were probably missing is SPEC extends Record<K, infer SPECK>, this tests if the type extends a record with the key K and puts the type of that key in SPECK.

You were not explicit about what happens on the else branches (if either type parameters is not an object, so I put never there, but you can customize as you need)

Let me know if I did not get it 100% right, we can make adjustments as needed.

Upvotes: 2

Related Questions