How can I write a typeguard that recognizes whether something is a Type<T>?

66 views Asked by At

I've got a type definition that allows for various ways of supplying an object:

type ObjSource<T> = InjectionToken<T> | Type<T> | ((context: IContext) => (T | Promise<T>)) | Promise<T> | T;

In there, IContext is an interface of my own that provides some context information about (a part of) my application.

To properly evaluate all of these, I need to find out what object source was actually supplied. I'm trying to do so with custom type guard functions. Let's start:

  • If the value is of type InjectionToken<T> or Type<T>, I'd simply like to stuff it into Angular's inject function, thereby letting Angular's dependency injection deal with it.
  • If the value is of type ((context: IContext) => (T | Promise<T>)), I'd like to execute that function and pass the result to Promise.resolve.
  • Otherwise, i.e. if the value is of type Promise<T> or T, I'd like to pass it to Promise.resolve.

Now, how can I distinguish these cases?

  • I think I can recognize an InjectionToken<T> by virtue of it being an instance of the InjectionToken class:
    function isInjectionToken<T>(x: ObjSource<T>): x is InjectionToken<T> {
      return x instanceof InjectionToken;
    }
    
  • I know next to nothing about T. I might be able to restrict it to T extends object. In that case, I distinguish (context: IContext) => (T | Promise<T>) from Promise<T> | T by the fact that typeof returns function for the former, but not the latter.

But how do I distinguish Type<T> from (context: IContext) => (T | Promise<T>)?

Angular's Type<T> is defined as:

class Type<T> extends Function {
  constructor(...args: any[]): T
}

Using a tiny sample Angular app, I've tried various implementations for isType<T>(x: ProviderToken<T>): x is Type<T> in here:

export class App {
  public doClick() {
    const a = isType(function blah() {
      return true;
    });
    const b = isType(new InjectionToken<string>('xyz'));
    const c = isType(App);

    alert(`${a} ${b} ${c}`);
  }
}

So far, though, I have not been able to find the right criteria to check:

  • x instanceof Type returns true for the third, but also the first case.
  • typeof x === 'function' (unsurprisingly) returns true for the first and the third case, as well.
  • typeof x === 'function' && Object.prototype.hasOwnProperty.call(x, 'constructor'), on the other hand, returns false in all cases.
0

There are 0 answers