Búsqueda de sitios web

angular - ¿Cómo puedo escribir un typeguard que reconozca si algo es un Type<T>?


Tengo una definición de tipo que permite varias formas de proporcionar un objeto:

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

Ahí, IContext es una interfaz propia que proporciona alguna información de contexto sobre (una parte de) mi aplicación.

Para evaluar correctamente todo esto, necesito averiguar qué objeto fuente se suministró realmente. Estoy intentando hacerlo con funciones de protección de tipo personalizadas. Empecemos:

  • Si el valor es del tipo InjectionToken<T> o Type<T>, simplemente me gustaría introducirlo en la función inject de Angular, permitiendo así que la inyección de dependencias de Angular lo gestione.
  • Si el valor es del tipo ((context: IContext) => (T | Promise<T>)), me gustaría ejecutar esa función y pasar el resultado a Promise.resolve.
  • De lo contrario, es decir, si el valor es del tipo Promise<T> o T, me gustaría pasarlo a Promise.resolve.

Ahora, ¿cómo puedo distinguir estos casos?

  • Creo que puedo reconocer un InjectionToken<T> por el hecho de ser una instancia de la clase InjectionToken:

    function isInjectionToken<T>(x: ObjSource<T>): x is InjectionToken<T> {
      return x instanceof InjectionToken;
    }
    
  • Sé casi nada sobre T. Podría ser capaz de restringirlo a T extends object. En ese caso, distingo (context: IContext) => (T | Promise<T>) de Promise<T> | T por el hecho de que typeof devuelve function para el primero, pero no para el segundo.

Pero ¿cómo distingo Type<T> de (context: IContext) => (T | Promise<T>)?

El Type<T> de Angular se define como:

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

Usando una pequeña aplicación de Angular, he probado varias implementaciones para isType<T>(x: ProviderToken<T>): x es Type<T> aquí:

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}`);
  }
}

Hasta ahora, sin embargo, no he podido encontrar los criterios correctos para verificar:

  • x instanceof Type devuelve true para el tercero, pero también para el primer caso.
  • typeof x === 'function' (sin sorpresa) también devuelve true para el primer y el tercer caso.
  • typeof x === 'function' && Object.prototype.hasOwnProperty.call(x, 'constructor'), por otro lado, devuelve false en todos los casos.