title: How to overload function in TypeScript with optional parameters?
body: <p>I have the following code that is partly converted to TypeScript (from JavaScript).</p>
  
  <p>Basically if a <code>callback</code> parameter exists, I always want the function itself to return <code>void</code>. Otherwise, return type <code>Promise&lt;object&gt;</code>. With an optional parameter (<code>settings</code>) before that (so technically the <code>callback</code> parameter could be passed in as the <code>settings</code> parameter, which the first few lines of the function handle that use case).</p>
  
  <p>For backwards compatibility purposes (and to keep code DRY), I do not want to create another function called <code>savePromise</code> or <code>saveCallback</code> and separate it out. I'm trying to figure out how to get TypeScript to be smart enough to understand this logic somehow.</p>
  
  <pre class="lang-js prettyprint-override"><code>type CallbackType&lt;T, E&gt; = (response: T | null, error?: E) =&gt; void;
  
  class User {
      save(data: string, settings?: object, callback?: CallbackType&lt;object, string&gt;): Promise&lt;object&gt; | void {
          if (typeof settings === "function") {
              callback = settings;
              settings = undefined;
          }
  
          if (callback) {
              setTimeout(() =&gt; {
                  callback({"id": 1, "settings": settings});
              }, 1000);
          } else {
              return new Promise((resolve) =&gt; {
                  setTimeout(() =&gt; {
                      resolve({"id": 1, "settings": settings});
                  }, 1000);
              });
          }
      }
  }
  
  const a = new User().save("Hello World"); // Should be type Promise&lt;object&gt;, should eventually resolve to {"id": 1, "settings": undefined}
  const b = new User().save("Hello World", (obj) =&gt; {
      console.log(obj); // {"id": 1, "settings": undefined}
  }); // Should be type void
  const c = new User().save("Hello World", {"log": true}); // Should be type Promise&lt;object&gt;, should eventually resolve to {"id": 1, "settings": {"log": true}}
  const d = new User().save("Hello World", {"log": true}, (obj) =&gt; {
      console.log(obj); // {"id": 1, "settings": {"log": true}}
  }); // Should be type void
  </code></pre>
  
  <p>I'm pretty sure the type file I'm aiming for would be something along the lines of the following. Not sure I'm accurate here tho.</p>
  
  <pre><code>save(data: string, settings?: object): Promise&lt;object&gt;;
  save(data: string, callback: CallbackType&lt;object, string&gt;): void;
  save(data: string, settings: object, callback: CallbackType&lt;object, string&gt;): void;
  </code></pre>
  
  <p>It seems like the <code>callback</code> parameter being passed in as the <code>settings</code> parameter use case can be handled by doing something like:</p>
  
  <pre><code>save(data: string, settings?: object | CallbackType&lt;object, string&gt;, callback?: CallbackType&lt;object, string&gt;): Promise&lt;object&gt; | void
  </code></pre>
  
  <p>But that is super messy, and from my experience it doesn't seem like TypeScript is smart enough to realize that <code>settings</code> will always be an optional object after those first 4 lines of code in the function. Which means when calling <code>callback</code> you have to type cast it, which again, feels really messy.</p>
  
  <p>How can I achieve this with TypeScript?</p>