1

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:

  • If INPUT and SPEC are objects, then Result should be an object with the keys of INPUT.
  • For every key of INPUT: Check if SPEC contains the same key
  • If true, then the value for this key is Result<INPUT[key], SPEC[key]>
  • If false, then the value for this key is true

Additional Information:

  • If INPUT and SPEC are objects, then SPEC is a subset of INPUT. This means: INPUT can be { foo: 42, bar: 42 }, but SPEC is allowed to only contain foo or bar or both or be empty.

Is this somehow possible with TypeScript?

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Btw. It looks like TypeScript knows what extends Record<...> means. You should be able to write this: SPEC extends Record<K, any> ? Result<INPUT[K], SPEC[K]>
@BenjaminM the compiler does not understand Record per se it understands the mapped type underlying it and figures out the relationship between the keys. But you are right, your way works too 😊

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.