Typescript 中私有方法的类型安全装饰器

Type-safe decorator for private method in Typescript

我试图在打字稿中实现一个只能应用于异步函数的装饰器。 如果装饰器应用于 none 异步函数,编译应该会引发错误。

这是我到目前为止尝试过的方法:


type AsyncFunction = (...args: any[]) => Promise<any>

function MyDecorator(/* params as needed */) {
    return function <P extends string>(target: Record<P, AsyncFunction> , propertyKey: P) {
        console.log("Implementation of MyDecorator")
    }
}

class SomeClassWithPrivateMethod {

    @MyDecorator()
    private async somePrivateMethod() {

    }

    @MyDecorator()
    async somePublicMethod() {

    }

}

问题是,它不适用于私有方法并出现以下错误消息:

Argument of type 'SomeClassWithPrivateMethod' is not assignable to parameter of type 'Record<"somePrivateMethod", AsyncFunction>'.   
Property 'somePrivateMethod' is private in type 'SomeClassWithPrivateMethod' but not in type 'Record<"somePrivateMethod", AsyncFunction>'.

我必须更改什么才能使其适用于私有方法?

TypeScript 不支持类型声明中的 'private'|'public'|'static' 修饰符。

您可以在class中声明static修饰符作为索引类型。参见 docs

Private 类型的处理方式与其他类型不同。您可以将其视为某种名义类型。

考虑这个例子:

type AsyncFunction = (...args: any[]) => Promise<any>

abstract class Base {
    private async somePrivateMethod() { }
}

function MyDecorator() {
    return function <P extends string>(target: Base, descriptor: PropertyDescriptor) {
        console.log("Implementation of MyDecorator")
    }
}

class SomeClassWithPrivateMethod {   
    @MyDecorator()
    private async somePrivateMethod() {}

    @MyDecorator()
    async somePublicMethod() { }

}

尽管 TS 具有结构类型系统,但您会收到此错误:

Argument of type 'SomeClassWithPrivateMethod' is not assignable to parameter of type 'Base'.
  Types have separate declarations of a private property 'somePrivateMethod'.(2345)

因此,Base的私有方法不等于SomeClassWithPrivateMethod

private方法

如果你从它们两个中删除 private 标志 - 它会编译。

因为不可能创建一个 type/class/interface 来表示 SomeClassWithPrivateMethod 的私有类型,所以您不能将此装饰器应用于私有方法或多或少是安全的,更重要的是 通用方式。

但是有一个解决方法。

您可以使用 SomeClassWithPrivateMethod class 类型作为 target 参数。它会起作用

type AsyncFunction = (...args: any[]) => Promise<any>

abstract class Base {
    async somePrivateMethod() { }
}

function MyDecorator() {
    return function <P extends string>(target: SomeClassWithPrivateMethod, prop: P, descriptor: PropertyDescriptor) {
        console.log("Implementation of MyDecorator")
    }
}

class SomeClassWithPrivateMethod {
    @MyDecorator()
    private async somePrivateMethod() { }

    @MyDecorator()
    async somePublicMethod() { }

}

或使用 target:unknown.

function MyDecorator() {
    return function <P extends string>(target: unknown, prop: P, descriptor: PropertyDescriptor) {
        console.log("Implementation of MyDecorator")
    }
}

对于我的用例,我找到了一个非常简单的解决方案
- 使用打字稿 4.6.3 测试 -

可以将描述符参数限制为特定类型,而不是对目标参数应用约束:

type AsyncFunction = (...args: any[]) => Promise<any>

function MyDecorator(/* params as needed */) {
    return function(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<AsyncFunction>) {
        console.log("Implementation of MyDecorator")
    }
}

TypedPropertyDescriptor<AsyncFunction> 是这里的关键。 TypedPropertyDescriptor 是来自打字稿的内置类型。