Repository: TypeScript
Issue: TypeScript 4.1+: Generic binding too broad in recursive conditional types
Labels: Needs Investigation

**TypeScript Version:**  4.2.0-dev.20201103
<!-- Search terms you tried before logging this (so others can find this issue more easily) -->
**Search Terms:** Recursive Conditional types, generics, tuple types

**Code**

```ts
// A test object
const obj = {
    a: '2',
    b: {
        c: 3,
        d: {
            e: 'string',
            f: {
                g: {
                    h: 4
                }
            }
        }
    }
} as const

// its type
type Struct = typeof obj;

/**
 * First recursive conditional type 
 * 
 * CheckArguments gets 
 * Obj - A nested object
 * Arugments - A tuple of keys to go down a nested path
 * 
 * The type is recursive, I check if the current argument lists extends keyof Obj --> Then the recursion ends
 * Otherwise, I check if the first value in the tuple is keyof Obj, infer the rest, and go down the same type
 * again
 */
type CheckArguments<Obj, Arguments> = 
    Arguments extends [keyof Obj] ? Obj[Arguments[number]] :
    Arguments extends [keyof Obj, ...infer U] ? CheckArguments<Exclude<Obj[keyof Obj], string | number>, U> : never;

/**
 * Tests, all 👍
 */
type Foo = CheckArguments<Struct, ['a']> // "2"
type Foo2 = CheckArguments<Struct, ['b', 'd', 'e']> // "string"
type Foo3 = CheckArguments<Struct, ['b', 'd', 'f', 'g', 'h']> // 4

/**
 * Second recursive conditional type
 * 
 * Arguments gets
 * Obj - A nested Obj
 * 
 * The recursive conditional type creates a union type of possible nested arguments in a tuple
 * This is based on Anders example from TSConf: https://github.com/ahejlsberg/tsconf2020-demos/blob/master/template/main.ts
 * (Dotted paths)
 */
type Arguments<Obj> = 
    Obj extends object ?
        [keyof Obj] | SubArguments<Obj, keyof Obj> :
        never;

// A helper type
type SubArguments<Obj, Key> =  Key extends keyof Obj ? [Key, ...Arguments<Obj[Key]>] : never;

// For example, the possible tuples of Struct 👍
type Bar = Arguments<Struct>;

// equals to this union type
type Bar2 = ["a" | "b"] | ["b", "c" | "d"] | ["b", "d", "e" | "f"] | ["b", "d", "f", "g"] | ["b", "d", "f", "g", "h"]

/**
 * So both recursive conditional types work on their own. A problem is once I want to combine
 * them in a function, where I expect the first argument to bind to a value type within the Arguments union
 * 
 * Instead of having just one value type passed to CheckArguments (the one that is bound through the generic Keys),
 * TypeScript passes all parts of the union to CheckArguments. This leads CheckArguments to return all possible values
 */

declare function get<Obj extends object, Keys extends Arguments<Obj>>(o: Obj, ...keys: Keys): CheckArguments<Obj, Keys>

/** 
 * Tests 💥
 * */
const foo = get(obj, 'a') // Should be "2" 😢 
const foo1 = get(obj, 'b', 'c') // Should be  3 😢 
const foo2 = get(obj, 'b', 'd', 'e') // Should be "string" 😢 
const foo3 = get(obj, 'b', 'd', 'f', 'g', 'h') // Should be 4 😢 
```

**Expected behavior:** `Keys` gets bound to the value type passed as an argument to the function. This value type is then used for CheckArguments

**Actual behavior:** `Keys` is the entire union type `Arguments<Obj>`, not the subset. This leads to `CheckArguments` returning a too broad return type (and taking very long to evaluate ;-))

**Playground Link:** [Click here](https://www.typescriptlang.org/play?ts=4.2.0-dev.20201103#code/PTAEEFQFwUwZyqA9gIwFYwMZQFCaQHYLLqgC8oA3jqLaAIYBcoA5AEwsA0NdKz1dQaEzMAzNyF0AJvx6TBMZiwQAnAJYEA5lznzaAM1l69mo8fMALZgBZd52gF878p3td0nDhnGGEEOHBBQNSgfKABPAAcYHAjo0ABlKBUAV2xyaCiYJH0SNABuAOAAKmKaYtAAMTUVYhUsFNq1ADcYXwIpELVCegAbTPjy0CGAYQssAGtwFU0UgFsYAlDQTRhloYB5UgBaCFACeFgpPKxcUArplM0FpZ9dyCgUyN62nNAJmHCwpBWfqSQAO4EBj7Q4wY6RehQCxDIYAFXGAzaah89UwjTgLRgnFAAElhONMBNgrloW10Sp6ksGDN5otEL0UcsYAAPWAdHwfcJvLZoUDbbYAPlACMW0ERaIx3WBiykcE2ZJUAJR2LxBMmJPFbX0NWIzT6KWRwLJ0CeL2CnM+PPQOI0+hgKi1oHqCBx9A6v1A-yBTrg9AWSKG9E09A05WAsSyoDGk2msxuoQAPLycXG6bdhRRdGmEz5Wey5aAANpc61oAC6oAA-KBeUWc-S4EWCPMUA7y5XGNnabnQPnZT4S1bcinQAA6Cd2h2gACqlZrMaJDduiYAoizML0UlIYMn0EPuSP0OWcaoNJpQAAffath2CnEz4XMA6tFSFQKleGHOBu3r9QC8G4AsjvhpG8SVEgPwUIuUw9o2iZJKk2A4kWLD0Cw5bCkEABEbBYaBbTgUgbAZNBy5JghaRQMhLAoFwrBSHRLAwOhmFgFhZ5aHhcQERBogkYSMHxnBFFIcWNGMQxOIsPojHaFJFgsaAQS2B+ZTnIkWCEMckpNK07SdFA0p9IG6lDGRPirKEmw7HsBwIOCtboLC6mis6DS6eSWldD0-TccI9RQvAIIpAQ0pIsguSREgcCYig5p2UcNJCbcwTAvQprPDELkWCiFqgCg9BwA5hAQB0Dp5iy-qZaA+gqEgcwigkIyEIYoAWFAUCRHAjAgJoIQWCkKBjvgczAPQ4xoL0cBtjMwChPgBD6GwAAMK3bDuczRcAcWoMAcyFbAKhzTAczPIFe2hgQY5WepAAUAAiSAdQ5kLQnAACUIF+eZe5oJmwyCLyfZsgOJzpFWzi0AeZaVteCSDT9o6lkef2gF2egvg675BJA4y9NEjrcfhiQI7BK6jgA0p8-2gFT4TAwWlqHo5fI1kWdM4hOY6I-udMYZ2oKvtjYDgY6rJVS8OImlFMVqHFbSPJlPhvCJiBAcTABC9COhQP2q4Kwt9gAjikfTfOKuUhWFRN+VrKjERQRZYfQWFXqAWEoFhsPFh7WE4lhmCu9eWFSF7btO57-uh-7MBB+7+hh9eEd++70fxynWGaInPuR6nGcJ-7Wf+xYXtFJ+6kJD8KBPRYbkUpiekLQZRm+VkPgAkgKjEiVZI1MgQJjnskR1fLDW5YQmBtPiALuogUA-CNKAaFlFRkmPaU1SF2DSjiALjPUaqstE6QmjqtSINryVz1XGjHPPIL6luCtRsq0IaE65mgFbhDORUuJELAegxw3gWHoM0c8oA0ApGIIQNoj9DThUhDFBy99SJk2WLdE0sDxRQjytXEKd8LB1SuLXE0qwDjqEwLTT4H0JAVDhFkBImB1CREQEgoqPg+j9EhCoZYbwTTf2ND8NBV84CDwRLlF4QCfAiPTMse+9RHgqDSn+UAMtYrmngfAECOAdybm1tqLehkSqWV+gzUGqAMCiTphVRmEB0FwF+oKQUt0kDMFHFzLk3VqFfHeswWRuZfo4hsYKMuFQvwIB8P+QApTtDGKBGBaxB9AQQyJZVxNpWBoXekpNiuE8B+EQMkpAABGVJax0loCkrRKSmAWDZKCKIfJACaoQQdiscpliqkSUYsxepbEOJZyaUk3iZSoAVK6VJSSrAZJSTkqwBSfTQAqSAA)

**Related Issues:** Did not find any.