有没有办法通过键入 TypeScript 方法装饰器来限制它可以装饰的方法的类型?
Is there a way to type a TypeScript method decorator to restrict the type of the method it can decorate?
我想编写一个 TypeScript 方法装饰器,它只能应用于具有特定类型的第一个参数的方法。这是我正在使用的代码库中的一种常见模式,用于传递具有数据库、指标、日志记录等句柄的请求上下文。我想编写一个装饰器,在请求上下文中需要这些资源之一,但是在其他方面与请求上下文的形状无关。
这是一个程式化的例子:
interface MyResource {
logMetricsEtc(...args: any): void;
}
interface HasResourceINeed {
myResource: MyResource;
}
function myDecorator<TFn extends ((tContext: HasResourceINeed, ...rest: any) => any)>(
_target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<TFn>,
): TypedPropertyDescriptor<TFn> | void {
const originalHandler = descriptor.value!;
descriptor.value = function (this: any, context: HasResourceINeed, ...inputs: any) {
context.myResource.logMetricsEtc(...inputs);
return originalHandler.apply(this, [context, ...inputs]);
} as TFn;
}
在使用中,启用 strictFunctionTypes
后,此装饰器在应用于看起来合理的方法时会导致编译错误:
interface ServiceContext {
myResource: MyResource;
otherResource: {
sendMessageEtc(msg: string): Promise<void>;
};
}
class MyBusinessClass {
// This causes a compile error, but shouldn't - the decorator will
// work here at runtime.
@myDecorator
async foo(context: ServiceContext, x: number): Promise<void> {
}
// This example MUST cause a compile error to prevent invalid
// usage - there's no resource available in the first arg for the
// decorator to use.
@myDecorator
async bar(y: string): Promise<void> {
}
}
不需要的编译错误如下所示:
Argument of type 'TypedPropertyDescriptor<(context: ServiceContext, x: number) => Promise<void>>' is not assignable to parameter of type 'TypedPropertyDescriptor<(tContext: HasResourceINeed, ...rest: any) => any>'.
Types of property 'value' are incompatible.
Type '((context: ServiceContext, x: number) => Promise<void>) | undefined' is not assignable to type '((tContext: HasResourceINeed, ...rest: any) => any) | undefined'.
Type '(context: ServiceContext, x: number) => Promise<void>' is not assignable to type '(tContext: HasResourceINeed, ...rest: any) => any'.(2345)
我无法合理地关闭 strictFunctionTypes
。有没有一种方法可以编写装饰器类型来接受 foo
但拒绝 bar
?
大概您希望 myDecorator()
的输入在修饰方法的第一个参数的类型 R
中是通用的,而不一定是在整个方法的类型 Fn
中。这将允许您接受第一个参数是 R
的某个子类型的方法,而不是 Fn
的子类型的方法(这意味着它们的参数必须是 supertypes of R
by method parameter contravariance and is not the constraint you want to apply).
也许像这样?
function myDecorator<R extends HasResourceINeed>(
_target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<((tContext: R, ...rest: any) => any)>,
): TypedPropertyDescriptor<((tContext: R, ...rest: any) => any)> | void {
const originalHandler = descriptor.value!;
descriptor.value = function (this: any, context: R, ...inputs: any) {
context.myResource.logMetricsEtc(...inputs);
return originalHandler.apply(this, [context, ...inputs]);
};
}
这似乎有效:
class MyBusinessClass {
@myDecorator // okay
async foo(context: ServiceContext, x: number): Promise<void> {
}
@myDecorator // error
async bar(y: string): Promise<void> {
}
}
好的,希望对您有所帮助;祝你好运!
我想编写一个 TypeScript 方法装饰器,它只能应用于具有特定类型的第一个参数的方法。这是我正在使用的代码库中的一种常见模式,用于传递具有数据库、指标、日志记录等句柄的请求上下文。我想编写一个装饰器,在请求上下文中需要这些资源之一,但是在其他方面与请求上下文的形状无关。
这是一个程式化的例子:
interface MyResource {
logMetricsEtc(...args: any): void;
}
interface HasResourceINeed {
myResource: MyResource;
}
function myDecorator<TFn extends ((tContext: HasResourceINeed, ...rest: any) => any)>(
_target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<TFn>,
): TypedPropertyDescriptor<TFn> | void {
const originalHandler = descriptor.value!;
descriptor.value = function (this: any, context: HasResourceINeed, ...inputs: any) {
context.myResource.logMetricsEtc(...inputs);
return originalHandler.apply(this, [context, ...inputs]);
} as TFn;
}
在使用中,启用 strictFunctionTypes
后,此装饰器在应用于看起来合理的方法时会导致编译错误:
interface ServiceContext {
myResource: MyResource;
otherResource: {
sendMessageEtc(msg: string): Promise<void>;
};
}
class MyBusinessClass {
// This causes a compile error, but shouldn't - the decorator will
// work here at runtime.
@myDecorator
async foo(context: ServiceContext, x: number): Promise<void> {
}
// This example MUST cause a compile error to prevent invalid
// usage - there's no resource available in the first arg for the
// decorator to use.
@myDecorator
async bar(y: string): Promise<void> {
}
}
不需要的编译错误如下所示:
Argument of type 'TypedPropertyDescriptor<(context: ServiceContext, x: number) => Promise<void>>' is not assignable to parameter of type 'TypedPropertyDescriptor<(tContext: HasResourceINeed, ...rest: any) => any>'.
Types of property 'value' are incompatible.
Type '((context: ServiceContext, x: number) => Promise<void>) | undefined' is not assignable to type '((tContext: HasResourceINeed, ...rest: any) => any) | undefined'.
Type '(context: ServiceContext, x: number) => Promise<void>' is not assignable to type '(tContext: HasResourceINeed, ...rest: any) => any'.(2345)
我无法合理地关闭 strictFunctionTypes
。有没有一种方法可以编写装饰器类型来接受 foo
但拒绝 bar
?
大概您希望 myDecorator()
的输入在修饰方法的第一个参数的类型 R
中是通用的,而不一定是在整个方法的类型 Fn
中。这将允许您接受第一个参数是 R
的某个子类型的方法,而不是 Fn
的子类型的方法(这意味着它们的参数必须是 supertypes of R
by method parameter contravariance and is not the constraint you want to apply).
也许像这样?
function myDecorator<R extends HasResourceINeed>(
_target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<((tContext: R, ...rest: any) => any)>,
): TypedPropertyDescriptor<((tContext: R, ...rest: any) => any)> | void {
const originalHandler = descriptor.value!;
descriptor.value = function (this: any, context: R, ...inputs: any) {
context.myResource.logMetricsEtc(...inputs);
return originalHandler.apply(this, [context, ...inputs]);
};
}
这似乎有效:
class MyBusinessClass {
@myDecorator // okay
async foo(context: ServiceContext, x: number): Promise<void> {
}
@myDecorator // error
async bar(y: string): Promise<void> {
}
}
好的,希望对您有所帮助;祝你好运!