title: How to infer correct type when returning `payload is T` in typescript?
body: <p>I have a type guard check function that does tells JavaScript that a value is a promise or not, and at the same time it tells TypeScript that the variable is a promise:</p>
  
  <pre><code>function getType (payload: any): string {
    return Object.prototype.toString.call(payload).slice(8, -1)
  }    
  
  function isPromise (payload: any): payload is Promise&lt;any&gt; {
    return getType(payload) === 'Promise'
  }
  </code></pre>
  
  <p>It works really well, however, I realised I need to not hard code the promise resolve to <code>any</code> but infer that type instead.</p>
  
  <p>This one example why I saw that <code>any</code> doesn't work properly:</p>
  
  <pre><code>export type PlainObject = { [key: string]: any }
  let a: PlainObject | PlainObject[] | Promise&lt;PlainObject | PlainObject[]&gt;
  let b = isPromise(a) ? await a : a
  </code></pre>
  
  <p>In this example, <code>b</code> is inferred as <code>PlainObject</code> while it SHOULD be <code>PlainObject | PlainObject[]</code>....</p>
  
  <p><strong>Question 1:</strong> Why is this happening?</p>
  
  <p>My attempted solution at a better <code>isPromise</code> function:</p>
  
  <pre><code>type Unpacked&lt;T&gt; =
      T extends (infer U)[] ? U :
      T extends (...args: any[]) =&gt; infer U ? U :
      T extends Promise&lt;infer U&gt; ? U :
      T;
  
  function isPromise2 &lt;T extends any&gt;(payload: T): payload is Promise&lt;Unpacked&lt;T&gt;&gt; {
    return getType(payload) === 'Promise'
  }
  </code></pre>
  
  <p>In theory I don't see why this wouldn't work. But I get this error:</p>
  
  <blockquote>
    <p>A type predicate's type must be assignable to its parameter's type.
      Type 'Promise>' is not assignable to type 'T'.
        'Promise>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'any'</p>
  </blockquote>
  
  <p><strong>Question 2:</strong> Why doesn't <code>payload is Promise&lt;Unpacked&lt;T&gt;&gt;</code> work and 
  how could I infer this otherwise?</p>
  
  <p>Here is everything above in the TypeScript PlayGround:</p>
  
  <p><a href="https://www.typescriptlang.org/v2/en/play?#code/KYDwDg9gTgLgBDAnmYcAKAbAhgSwHYDyARgFbADG8AvHAN5wDaA1sIgFxwDOMU+A5gF0OWPIjgBfAFCSAZgFc8lHBDxw+wGABVkqABRgsiDBCwATYaICUHbrzx86kuHCga5UVcTKUAdGCgQMIE6PkEAyjz8PuRYGBj6hsZmlj6cGDjkwLoAHAA0cAC0AIyWkuLOztLyijDKqjicaAEAtg16BkYm5nAiiNZwHUmmcA3oLW0APL0AfI7OrjDuqupaOgmdyXBU23AA5E0QrZzAu2XSGBo9HJi4hKQU8AA+6Nj4Xg8MAnDPB0fAEzc3vdKN8Xrd3pRPtNJBd4EQtiNGuNjrosJY4AB+HoAd1w8CwcGE0gA9MS4NiABZYeAASRcwFiGDEuLw8CCcFMEERbEkSBQcAAqngDOQWKYJppZlQnBVNHBQDBgHhTJw4Lp8DJgFBBZZPpjBYSZc45QqlSq1T5LVgoHxOBZEJ90VRZhqtQasQLDRU4CaQIrlarfpNXdqBbMPV7ZQBuKoKJQqRFB44AJjgEvlfrNqpm6yGHE0-UGXURY0OkyFIrFEums1oMoWSzUGm0KFzXSdO32yJOZyAA" rel="nofollow noreferrer">https://www.typescriptlang.org/v2/en/play?#code/KYDwDg9gTgLgBDAnmYcAKAbAhgSwHYDyARgFbADG8AvHAN5wDaA1sIgFxwDOMU+A5gF0OWPIjgBfAFCSAZgFc8lHBDxw+wGABVkqABRgsiDBCwATYaICUHbrzx86kuHCga5UVcTKUAdGCgQMIE6PkEAyjz8PuRYGBj6hsZmlj6cGDjkwLoAHAA0cAC0AIyWkuLOztLyijDKqjicaAEAtg16BkYm5nAiiNZwHUmmcA3oLW0APL0AfI7OrjDuqupaOgmdyXBU23AA5E0QrZzAu2XSGBo9HJi4hKQU8AA+6Nj4Xg8MAnDPB0fAEzc3vdKN8Xrd3pRPtNJBd4EQtiNGuNjrosJY4AB+HoAd1w8CwcGE0gA9MS4NiABZYeAASRcwFiGDEuLw8CCcFMEERbEkSBQcAAqngDOQWKYJppZlQnBVNHBQDBgHhTJw4Lp8DJgFBBZZPpjBYSZc45QqlSq1T5LVgoHxOBZEJ90VRZhqtQasQLDRU4CaQIrlarfpNXdqBbMPV7ZQBuKoKJQqRFB44AJjgEvlfrNqpm6yGHE0-UGXURY0OkyFIrFEums1oMoWSzUGm0KFzXSdO32yJOZyAA</a></p>