typescript - Er det mulig å gi typesikkerhet til alternativene for metode-dekoratører


Jeg bestemte meg for å skrive noen nyttige dekoratører som memoize, rateLimiter. Jeg ønsker å oppnå så mye typesikkerhet som mulig uten unødvendig ekstraarbeid.

Er det mulig å sikre full typesikkerhet i dekoratører som dette uten manuelt spesifiserte generiske typer?

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

Svar #1

Dette er et kjent problem i TypeScript, uten noen åpenbar løsning (annet enn å manuelt spesifisere generiske typeparameter).

Problemet med å implementere dette, som forklart i denne kommentaren av @DanielRosenwasser, er at bruk av dekoratører er som å kalle en kurert funksjon, og den typen generisk inferens du ønsker ville vært slik:

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

som ikke fungerer fordi TypeScript utleder den generiske typen når funksjonen f blir kalt på callback-argumentet sitt, og ikke venter til den returnerte funksjonen selv blir kalt. Så når T-typen faktisk ville vært kjent av kompilatoren, er det for sent, og den har allerede mislyktes med å utlede riktig.

Jeg vet ikke om jeg har noen råd annet enn å fortsette å spesifisere argumenter manuelt, og kanskje gå til det problemet i TypeScript og gi det en 👍 eller beskrive ditt brukstilfelle hvis du synes det er mer overbevisende enn de andre som er nevnt. Lykke til!

Relaterte artikler