问题
I decided to write some utility decorators such as memoize
, rateLimiter
. I want to achieve as much type safety as possible without unnecessary boilerplate code.
Is it possible to ensure full type safety in decorators like that without manually specified generics?
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;
}
}
回答1:
This is a known issue in TypeScript, with no obvious solution (other than manually specifying generic type parameters).
The problem with implementing this, as explained in this comment by @DanielRosenwasser is that using a decorators is like calling a curried function, and the kind of generic inference you want would be like this:
declare let f: <T>(callback: (x: T) => void) => (y: T) => void;
f(x => x.a)({ a: 100 }); // error!
// ~ <-- T is inferred as {} or unknown,
which doesn't work because TypeScript infers the generic type when the function f
is invoked on its callback argument, and does not wait until the returned function is itself called. So by the time the T
type would actually be known be the compiler, it's too late and it's already failed to infer properly.
I don't know if I have any advice other than to continue to manually specify arguments, and maybe go to that issue in TypeScript and give it a 👍 or describe your use case if you think it's more compelling than the other ones mentioned. Good luck!
来源:https://stackoverflow.com/questions/56403431/is-it-possible-to-provide-type-safety-to-method-decorator-options