1

Consider

interface MagicMonkey {
   goCrazy(): Magic
   goCrazy(): Magic & string
   dance(): Magic
   dance(): Magic & string
   state: "Full" | "Hungry" | "Tired"
}

I want to create a mapped type when applied to the interface above results in something that strictly enforces the below interface

interface MagicMonkey {
    goCrazy: Magic & string
    dance: Magic & string
}

I've managed to map all the return types by isolating the methods using the trick that was suggested in this question. Like so:

type AnyMethod = (...args: any[]) => any;

type MatchingPropertyKeys<T, U> = ({
    [P in keyof T]: T[P] extends U ? P : never
})[keyof T];

type JustMethodKeys<T> = MatchingPropertyKeys<T, AnyMethod>;
type JustMethods<T> = Pick<T, JustMethodKeys<T>>;

type MagicMonkeyMethods = JustMethods<MagicMonkey>

type MagicMonkeyMethodReturnTypes = {
   [P in keyof MagicMonkeyMethods]: ReturnType<MagicMonkeyMethods[P]>
}

But it seems to be happy with either implementation of the overloaded methods return types, so the interface below also adheres to the type.

interface MagicMoneyMethods {
   goCrazy: Magic & string;
   dance: Magic;
}

Which makes sense. But nothing I've tried to apply to filter it seems to work. Because I always end up with a set of keys I can use to Pick from the original type, which just gets me back to the same place.

  • `ReturnType` will pick just one overload return type (the last one I believe). You can extract multiple return types but I am not sure what rule you want to enforce when picking a particular return type from the overload set. – Titian Cernicova-Dragomir Dec 01 '18 at 20:14
  • @TitianCernicova-Dragomir I think it's actually picking both. Because if I implement either the compiler is happy... I'm mocking out [Webdriverio](http://webdriver.io/api/protocol/elementIdText.html) which has a fluent API. So it returns a version of itself on each call but also the response data, I just care about the response data part. I want to enforce that my mocks return data of the same type as the real call would. For example `elementIdText` returns `Client> & RawResult` I only care about `RawResult` – Marthinus Engelbrecht Dec 01 '18 at 20:45
  • @TitianCernicova-Dragomir If you have a reliable method for detecting number of overloads and extracting each return type, I'd be interested. I keep getting tripped up trying to distinguish a signature which returns a `{}` and one which isn't an overload (because the failed inferred type becomes `{}`) – jcalz Dec 01 '18 at 21:41
  • @jcalz I don't have anything particularly great, just the same idea as here: https://stackoverflow.com/questions/52760509/typescript-returntype-of-overloaded-function/52761156#52761156. – Titian Cernicova-Dragomir Dec 01 '18 at 22:33
  • 1
    @MarthinusEngelbrecht `MagicMonkeyMethodReturnTypes` is resolved to exactly what you want: `type MagicMonkeyMethodReturnTypes = { goCrazy: Magic & string; dance: Magic & string; }`. And I get errors if I try assignments to the `MagicMoneyMethods` interface. Why do you way it's happy with it ? Does the `Magic` class contain any members maybe that is what is tripping up compatibility (an empty class would not much count towards assignability) https://stackblitz.com/edit/typescript-nbgpd3 – Titian Cernicova-Dragomir Dec 01 '18 at 22:49
  • @TitianCernicova-Dragomir Yeah, you are right. This example was contrived. The real code is a bit more complex. What tripped me up was that the order of the overloads are different in the real situation. `Magic & string` is first and then `Magic`, in which case it would seem like it's returning both, but actually, it's just that `Magic & string` is assignable to `Magic`. – Marthinus Engelbrecht Dec 02 '18 at 03:03

0 Answers0