3 votes

Est-il possible d'assurer la sécurité des types pour les options des décorateurs de méthodes ?

J'ai décidé d'écrire quelques décorateurs utilitaires tels que memoize , rateLimiter . Je veux obtenir autant de sécurité de type que possible sans code inutile de type "boilerplate".

Est-il possible d'assurer une sécurité totale des types dans des décorateurs de ce type sans spécifier manuellement des génériques ?

type GET_FUNCTION_SIGNATURE<
  T extends TypedPropertyDescriptor<any>
> = T extends TypedPropertyDescriptor<infer U> ? U : never;

interface ITestDecoratorOptions<DECORATED_FUNCTION_ARGUMENTS_TYPE, DECORATED_FUNCTION_RETURN_TYPE> {
  getKeyFromArgs: (args: DECORATED_FUNCTION_ARGUMENTS_TYPE) => string;
  getDefaultValue: (args: DECORATED_FUNCTION_ARGUMENTS_TYPE) => DECORATED_FUNCTION_RETURN_TYPE;
}

const testDecorator = <TYPED_PROPERTY_DESCRIPTOR extends TypedPropertyDescriptor<any>>(
  options: ITestDecoratorOptions<
    Parameters<GET_FUNCTION_SIGNATURE<TYPED_PROPERTY_DESCRIPTOR>>,
    ReturnType<GET_FUNCTION_SIGNATURE<TYPED_PROPERTY_DESCRIPTOR>>
  >
) => {
  return (
    target: Object,
    key: string,
    descriptor = Object.getOwnPropertyDescriptor(target, key) as PropertyDescriptor
  ): TYPED_PROPERTY_DESCRIPTOR => {
    return null as any;
  };
};

class Test {
  //             \/ Is it possible to remove that generic and keep full type safety here?
  @testDecorator<TypedPropertyDescriptor<(a: number, b: string) => boolean>>({
    getKeyFromArgs: args => {
          // number               string
      return args[0].toString() + args[1]; // full type checking
    },
    getDefaultValue: args => {
      // full type checking: on args(number, string) and return type(boolean)
      if (args[0] === 1) {
        return true;
      }

      return false;
    }
  })
  public someMethod(a: number, b: string): boolean {
    return true;
  }
}

2voto

jcalz Points 30410

Il s'agit d'un problème connu dans TypeScript, sans solution évidente (autre que la spécification manuelle des paramètres de type générique).

Le problème de cette mise en œuvre, tel qu'il est expliqué dans le document ce commentaire de @DanielRosenwasser est que l'utilisation d'un décorateur revient à appeler un au curry et le type d'inférence générique que vous souhaitez serait le suivant :

declare let f: <T>(callback: (x: T) => void) => (y: T) => void;
f(x => x.a)({ a: 100 }); // error!
//     ~ <-- T is inferred as {} or unknown, 

ce qui ne fonctionne pas car TypeScript déduit le type générique lorsque la fonction f est invoquée sur son argument callback et n'attend pas que la fonction retournée soit elle-même appelée. Ainsi, au moment où la fonction T serait en fait connu du compilateur, il est trop tard et il n'a déjà pas réussi à déduire correctement.

Je ne sais pas si j'ai un autre conseil à donner que de continuer à spécifier manuellement les arguments, et peut-être d'aller à cette question dans TypeScript et de lui donner un ou de décrire votre cas d'utilisation si vous pensez qu'il est plus convaincant que les autres mentionnés. Je vous souhaite bonne chance !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X