如果没有替换,如何向TypeScript解释该方法是否被增强

问题是我想使用一种修饰的方法,该方法从修饰器中检索一个属性,该修饰器依次接收方法的参数并生成一些东西,并用该东西的结果调用修饰的方法。

下面是一个示例https://stackblitz.com/edit/angular-rkojji

export function Augment(
  target: any,propertyKey: string,descriptor: PropertyDescriptor
) {
  const method = descriptor.value;
  descriptor.value = function(message: string) {
    return method.call(this,{
      data: message
    });
  };
  return descriptor;
}

@Component({
  selector: "my-app",templateUrl: "./app.component.html",styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "Angular";

  @Augment
  decorated(data: string) {
    return data.data;
  }

  replacedMethod(val: string) {
    console.log(val);
    return val;
  }
}

在函数Augment中,我捕获了参数并将计算结果(为简单起见,这里只是将对象包装)应用于装饰方法。

问题在于TypeScript确保在接收到string的同时用object调用了该方法。 TypeScript编译器对此有所抱怨,但是代码本身正在运行。我不想抑制所有装饰器调用。

所有这些努力实际上是为了简化API调用,使之类似

@Post
users(data: any): Observable<any> {
  return data.pipe(
    mapTo(null)
  );
}

并将其命名为users({page: 1})。这样装饰器将users作为端点路径,并使用提供给users的有效负载对其进行调用,并使用users执行http.post,因此users现在是可管道的运算符

sunshine7993 回答:如果没有替换,如何向TypeScript解释该方法是否被增强

作为装饰器do not mutate the types of the things they decorate (see microsoft/TypeScript#4881),使用装饰器基本上不可能做到这一点。因此,即使您在修饰带有string参数的方法时,即使您在运行时进行了更改,编译器仍会将其视为带有string参数的方法。 microsoft / TypeScript#4881中对此进行了大量讨论,双方都怀有强烈的感情,但最终unlikely anything will change in TypeScript直到the analogous proposed JavaScript feature成熟到足以稳定为止。


相反,您可能想考虑使用类似装饰器的函数,该函数使用一个方法接收类构造函数,然后使用增强方法将其转换为另一个类构造函数。它就像一个装饰器,因为您没有将其与@表示法一起使用;相反,您可以像class expressionsubclass一样使用它。用类表达式替换 class 装饰器更为简单,但是您也可以使用方法装饰器来做一些工作。

这是一个例子。请注意,以下代码只是一个示例。它尚未经过全面测试,并且在所有情况下都可能无法达到您的期望。为了安抚编译器,它进行了大量类型杂耍和类型断言:

type AugmentMethod<
  T extends Record<K,(...args: any) => any>,K extends keyof T,A extends readonly any[]> = { [P in keyof T]:
    P extends K ? (...args: A) => ReturnType<T[P]> : T[P]
  };

function AugmentMethod<
  C extends new (...args: any) => Record<K,T extends InstanceType<C>,K extends keyof any,A extends readonly any[],>(
    ctor: C,methodName: K,wrapMethod: (...args: A) => Readonly<Parameters<T[K]>>
  ) {

  return class extends (ctor as any) {
    constructor(...args: any) {
      super(...args);
    }
    [methodName](...args: A) {
      return super[methodName](...(wrapMethod(...args) as any[]));
    }
  } as (new (...args: ConstructorParameters<C>) => AugmentMethod<T,K,A>) &
    { [P in keyof C]: P extends "prototype" ? AugmentMethod<T,A> : C[P] };
}

因此类型AugmentMethod具有三个类型参数:“基”类实例类型T,该类的方法名称K和新参数列表类型{{1 }}。它返回“子”类实例类型的类型,其属性与A的属性相同,除了名为T的方法采用类型为K的参数外。

函数A具有三个参数:“基”类构造函数值,该值构造类型为AugmentMethod的实例,方法名称为类型T为该类提供了一个参数包装函数,该包装函数接受K类型的参数并返回要传递给“基本”类构造函数的参数的有序数组。然后,它返回一个实例为A的“子”类构造函数:与“基本”实例相同,不同之处在于其扩展方法希望将一组参数传递给上面的参数包装函数。

这类似于您的示例代码:

AugmentMethod<T,A>

因此class AppComponent { name = "Angular"; decorated(data: { data: string }) { console.log("Got " + JSON.stringify(data)); return data.data; } replacedMethod(val: string) { console.log(val); return val; } } const a = new AppComponent(); const sa = a.decorated({ data: "hi" }); // Got {"data":"hi"}; console.log(sa); // hi 类保持不变,并且其AppComponent方法期望使用类型为decorated()的参数。让我们使用{data: string}类表达式:

AugmentMethod

因此,您可以看到class WrappComponent extends AugmentMethod(AppComponent,"decorated",(data: string) => [{ data }] as const) { } const w = new WrappComponent(); const sw = w.decorated("hey"); // Got {"data":"hey"}; console.log(sw); // hey 的行为类似于WrappComponent的版本,其AppComponent方法已被decorated()函数“修饰”。


好的,希望能有所帮助;祝你好运!

Link to code

本文链接:https://www.f2er.com/3010950.html

大家都在问