0

I have a function that can return 1 out of 3 possible types:

getFoo(): Observable<Foo> | Promise<Foo> | Foo {

    //return 1 of 3 types

}

So how can I ensure the exact type after calling:

const foo = someClassObj.getFoo();

If I now want to get the data from foo, how can I know if it's Observable, Promise, or class instance Foo? When I try instanceof or typeof, they only detect 'object'.

u84six
  • 4,604
  • 6
  • 38
  • 65
  • 2
    You could just test for the methods, `.then` and `.subscribe`, see e.g. https://stackoverflow.com/q/27746304/3001761. But it might be easier to rewrite that to be more consistent. – jonrsharpe Sep 23 '20 at 18:43
  • 1
    rxjs exports a method `isObservable() ` that you can use. For checking if it is a promise, refer to this answer https://stackoverflow.com/a/27746373/6513723 – pascalpuetz Sep 23 '20 at 18:44
  • 3
    "instanceof or typeof, they only detect 'object'" those are **runtime** constructs, they *know nothing about your compile-time static types*, nor can you access the runtime value at compile time. – Jared Smith Sep 23 '20 at 18:47
  • @JaredSmith this is must be a limitation with TypeScript because mostly all type safe languages have a way to check objects for class type at runtime. I'm assuming it's because TypeScript is a superset of JS – u84six Sep 23 '20 at 22:09
  • @u84six first off if by type-safe you mean "sound" there are very few "type-safe" languages in existence and none of them are widely used in production. As for statically typed languages in general many of them do not retain all or even any type information at runtime: the types compile away. Even so, on the other side there's no way the compiler can know about the runtime type of your values like you are suggesting. – Jared Smith Sep 23 '20 at 22:19
  • @JaredSmith I use type safety to imply a strong type system. There are plenty of languages that a strong typed. And if you scroll down, you'll see TypeScript with a big question mark: https://en.wikipedia.org/wiki/Comparison_of_programming_languages_by_type_system – u84six Sep 24 '20 at 13:07
  • @u84six "stongly typed" [does not have an agreed-upon meaning](https://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/) the way statically typed does, so that list reflects only the judgement call of whomever wrote it. Is it strongly typed or not if it has automatic conversion when you add an int and a float? Different precisions of integer values? Array bounds-checking? Even among those on that list on wikipedia, very few of them have complete runtime type information... – Jared Smith Sep 24 '20 at 13:31
  • ...For instance: C++ only has rtti for generics and primitives. Haskell requires any user-defined types to be tagged for any rtti, SML IIRC retains type info only about top level forms, etc. Claiming that Typescript is somehow deficient compared to other languages in this regard is disingenuous and retaining that info would be undesirable as a default because of the bandwidth constraints (would certainly be nice to have as a compiler option for development though). – Jared Smith Sep 24 '20 at 13:31

2 Answers2

2

You can use instanceof keyword.

const foo: any = someClassObj.getFoo();

if (foo instanceof Observable) {
} else if (foo instanceof Promise) {
} else if (foo instanceof Foo) {
}
Derek Wang
  • 10,098
  • 4
  • 18
  • 39
  • Will the last one also work if Foo is just an interface..? – MikeOne Sep 23 '20 at 19:02
  • Yes. works. https://alligator.io/typescript/instanceof-guard/ – Derek Wang Sep 23 '20 at 19:03
  • This does not work for me. I can see in the debugger when the function returns an Observable, foo instanceof Observable returns false. The only thing that resolves to true is 'foo instanceof Object'. Also, I can't even evaluate the class object Foo because of a syntax error 'Foo only refers to a type but is being used as a value here' – u84six Sep 23 '20 at 19:14
  • For Observable, you can use `isObservable` function in `rxjs` module – Derek Wang Sep 23 '20 at 19:18
  • @Derek.W ok, that works. That gets me 1/3 or the way there. I suppose for promise, I can see if .then is defined. So that just leaves me with checking for the custom Foo type. This means I would have to add something like a type guard, which I hate doing – u84six Sep 23 '20 at 19:22
  • @u84six if you hate using type guards to narrow then don't use a 3-way union type use polymorphism or function overloads. – Jared Smith Sep 23 '20 at 22:22
0

Ok, so this is what I had to do. Thanks to Derek W, I was able to use isObservable function in rxjs. So then it got me thinking about isPromise, and wouldn't you know, Angular 8 has a built-in utility function for that. So the final code is:

if(isObservable(foo)) {
   //handle observable
} else if(isPromise(foo)) {
   //handle promise
} else if(foo !== null) {
   //handle instance of Foo
} else {
   //handle null
}
u84six
  • 4,604
  • 6
  • 38
  • 65