title: Recursively get all keys using mapped type
body: <p>I know this question has been asked before, and that there are working approaches, such as <a href="https://stackoverflow.com/a/65129268/6576953">this</a>, but I have a specific question about why a certain implementation using mapped types does not work:</p>
  <p>When trying to recursively get all keys of a nested object, you need to handle the case of an array, so I wrote this type:</p>
  <pre><code>type KeysDeep&lt;T&gt; = T extends object
    ? {
        [K in keyof T]-?:
          | K
          | (T[K] extends (infer A)[] ? KeysDeep&lt;A&gt; : KeysDeep&lt;T[K]&gt;);
      }[keyof T]
    : never;
  </code></pre>
  <p>And this seems to work well, <em>except</em> if a property with an array type is optional, e.g.</p>
  <pre><code>type Foo = {
      foo: string;
  }
  
  type Bar = {
      bar: string;
      foos: Foo[];
  }
  
  type Baz = {
      baz: string;
      maybeFoos?: Foo[];
  }
  
  type Good = KeysDeep&lt;Bar&gt;; // &quot;foos&quot; | &quot;bar&quot; | &quot;foo&quot;
  type Bad = KeyDeep&lt;Baz&gt;; // &quot;foos&quot; | &quot;bar&quot; | &quot;foo&quot; | ... a zillion more
  </code></pre>
  <p>I would have thought the <code>?-</code> operator would have adequately handled the optional property here. I've tried modifying this type in several ways but have been unable to make it work. Is there any way to fix this issue?</p>
  <p>In the end, it's fine to use the solution I linked above, however it has poorer intellisense. e.g. the intellisense tooltip will list something like <code>&quot;foo&quot; | keyof Bar</code> instead of <code>&quot;foos&quot; | &quot;bar&quot; | &quot;foo&quot;</code>.</p>
  <p>Anyway, this isn't a huge deal, but I'm just curious if there's a fix and as to why this doesn't work. Thanks in advance.</p>
  <p>Here's a <a href="https://www.typescriptlang.org/play?ssl=30&amp;ssc=2&amp;pln=18&amp;pc=1#code/C4TwDgpgBA0hIGcAiEJgEICcD2BrCAdgDwAqAfFALxQlQQAewhAJglNgEYBWEAxsACgoUAPxQA3kOHSA2jCgBLAlHwhsAMxoBdALQiAXFOnSAPrCPGoZuIhRoseQqTlayAbgsBfGao3ap%20lAEEABuEJgeAqCQsPDIqGAAMhAICA74xORUNHSMLGycPPxSYpKWUHKKyr6aJLoGFsbWjaZQABQkLrlMBKxQmBAAhszYBAA2IO1K6uFQAIIAlDJaorG2Ccmp6U5zFIE28WibaTgZzjCuCx7G3jX%20woHBYRECr9HQAGLY2Nll0urfQIIYCYJQAcw8nje4Gg6EGmF%20Fg48KBIPB13%203wQgS%202GWkOhMThAC9EcZkcTUaCCBCLABbQYgDgQXEIAxQXH4gRQqIwqBw5jZA52DCnJxwzDuXkxADi30F1GFGxSJ0cxAlUve-OGczBgyUQriIuO23Vg2JUul0CVaAA6thMLhwaQKNRaAwen1CnxgKs2hZ3XlemwBsNRhMpgQZgiAKpLFZiG1ge2O50xvbtFrCO60axGhIpp005w51wWBYCBZQR6hcKE2EpX2K-N2h1FsFEEnuIA" rel="nofollow noreferrer">playground</a> link with a full example.</p>