typescript - 在变量打字稿上动态调用静态方法 - 理论/实现
问题描述
我正在使用 Ionic 4 和 Angular 7 创建一个混合移动应用程序。这个应用程序将与我用 PHP (Yii2) 开发的 API 进行通信,以向用户显示数据。
我想创建一个通用的 REST API 类来与服务器进行通信,并且我已将本文用作我的开发的基线(https://medium.com/@krishna.acondy/a-generic-http-service-approach -for-angular-applications-a7bd8ff6a068)。
根据我阅读其他多篇文章和文档的理解,该文章中的序列化程序类示例应将方法声明 fromJson()
为toJson()
静态。我习惯在 PHP 中做这种事情。由于该对象没有状态并且这些本质上是辅助函数,因此当我真的只使用这些辅助函数时,我不希望这些类的多个实例出现(帮助我理解我是否错了)。
但是,在rest.service.ts
文件中,这些函数是在实例 ( this.serializer.fromJson(data)
) 上调用的。要从我读过的内容中调用静态方法,我需要像这样调用它,ClassName.staticMethod()
但在上下文中rest.service.ts
我不能这样做,因为我不知道如何在变量 ( this.serializer.staticMethod()
) 上动态调用静态方法。在 PHP 中,我习惯于调用$className::staticMethod()
这是我试图在这里找到的等价物,但似乎不可能。
我发现这个问题(动态调用静态方法)似乎触及了我想做的事情,但似乎必须有更好的方法。在我的每个类中都这样做,XYZ.service.ts
我将不得不使用序列化器类的静态方法写出相同的映射,这似乎在重复自己,让我认为必须有更好的实践来实现我想要实现的目标.
所有这一切的另一个原因是,在我的序列化程序中,有时我需要为子资源引用另一个序列化程序。现在我正在创建一个实例来执行此操作,但这似乎效率低下,并且进一步让我认为我应该找到一种静态执行此操作的方法,因为我可能有多个XYZSerializer
创建实例只是为了能够运行我觉得的方法应该是静态的。
这是我现在的工作代码示例,仿照上述文章,为便于阅读而缩写:
export interface Serializer {
fromJson(json: any): ApiResource;
toJson(resource: ApiResource): any;
}
export class SharkBiteSerializer implements Serializer{
playerSerializer: PlayerSerializer = new PlayerSerializer;
fromJson(json: any): SharkBite {
const sharkBite = new SharkBite();
sharkBite.id = json.id;
...
sharkBite.featuredPlayer = this.playerSerializer.fromJson(json.featuredPlayer);
return sharkBite;
}
toJson(sharkBite: SharkBite): any {
return {
id: sharkBite.id,
...
};
}
}
export class SharkBiteService extends RestService<SharkBite> {
endpoint = 'shark-bites'
serializer = new SharkBiteSerializer;
constructor(
httpClient: HttpClient,
)
{
super(httpClient);
}
}
export abstract class RestService<T extends ApiResource> {
abstract endpoint: string;
abstract serializer: Serializer;
constructor(
private httpClient: HttpClient,
) {}
/**
* Add a new record
*/
public create(item: T): Observable<T> {
return this.httpClient
.post<T>(`${environment.apiUrl}/${this.endpoint}`, this.serializer.toJson(item))
.pipe(
map(data => this.serializer.fromJson(data) as T)
);
}
/**
* Update an existing record
*/
public update(item: T): Observable<T> {
return this.httpClient
.put<T>(`${environment.apiUrl}/${this.endpoint}/${item.id}`, this.serializer.toJson(item))
.pipe(
map(data => this.serializer.fromJson(data) as T)
);
}
/**
* Retrieve a single record
*/
public read(id: number): Observable<T> {
return this.httpClient
.get(`${environment.apiUrl}/${this.endpoint}/${id}`)
.pipe(
map(data => this.serializer.fromJson(data) as T)
);
}
/**
* Retrieves muleiple records based on a query
*/
public list(httpParams: HttpParams): Observable<T[]> {
return this.httpClient
.get(`${environment.apiUrl}/${this.endpoint}`, {params: httpParams})
.pipe(
map((data: any) => this.convertData(data))
);
}
/**
* Delete a single record
*/
public delete(id: number) {
return this.httpClient
.delete(`${environment.apiUrl}/${this.endpoint}/${id}`);
}
/**
* Converts the array of items into an array of the typed items
*/
private convertData(data: any): T[] {
return data.map(item => this.serializer.fromJson(item));
}
}
为了尝试重申我正在尝试做的事情,我想拥有fromJson
并toJson
成为静态方法。在rest.service.ts
我应该打电话SerializerClassName.toJson()
和SerializerClassName.fromJson()
; 然后在SharkBiteSerializer
我可以静态调用。我玩弄了一个抽象类来让方法是静态的,但我似乎无法弄清楚如何在变量上调用静态方法。PlayerSerializer.toJson()
PlayerSerializer.fromJson()
Serializer
如果我在这里没有遵循最佳实践,请赐教。我是 Typescript 的新手,我意识到我的 PHP 背景可能正在渗透。我只是想找到实现这一点的最佳方法,并愿意接受任何建议。谢谢!
#
更新:
我理解我不能在类的实例上调用静态方法的概念。我想知道是否可以以任何方式将类名存储在变量中,然后对存储在该变量中的类名调用静态方法。例如,在 PHP 中,我可以:
$className = '\namespace\for\ClassName';
echo $className::staticMethod();
我想要一些方法来在每个休息服务上存储序列化程序类的名称,并让它从该序列化程序类调用静态函数。或者,如果有更好的方法来实现我正在寻找的东西,我愿意接受想法。
解决方案
简短的回答是,你不能——你不能在变量上调用静态方法,因为在 TypeScript 上,静态方法属于类,而不是变量。
也就是说,你可以破解它,因为 TypeScript 变成了 JavaScript。它不干净,因为您将失去使用 TypeScript 实现的适当类型,但至少它使您能够继续您的思维框架。
下面的TS代码...
class Foo {
public static bar() {
console.log("bar");
}
}
Foo.bar();
...编译为
var Foo = /** @class */ (function () {
function Foo() {
}
Foo.bar = function () {
console.log("bar");
};
return Foo;
}());
Foo.bar();
这意味着 bar 不在原型上(因此它没有被继承),而是留在类本身上。
访问类函数的方式是通过构造函数属性,所以:
class Foo {
public static bar() {
console.log("bar");
}
}
function testIt(f: Object) {
(f.constructor as any).bar();
}
let f = new Foo();
testIt(f);
同样,不干净,但这是一种方式。
现在,这里的大问题是:为什么你真的需要一个静态方法?我想它有一些习惯(上帝知道我写了很多静态的东西,直到我记得我不应该),但通常没有必要甚至不建议这样做。静态方法是过程编程的泄漏,在面向对象代码中几乎没有位置。您从中获得的收益很少(当然,分配更少,但即使这不适用于您的情况),而您从常用方法中获得了很多好处。
在你的情况下,正如你在文章中看到的那样,你应该Serializable
像你说的那样定义一个接口(我称之为Jsonable
更符合当前趋势)并在那里定义这两个方法。现在你有一种在每个对象中调用 then 的简单方法。您可以创建一个基类 ( abstract class Jsonable
),该基类具有可供这些类使用的基本逻辑。抽象类应该包含通用逻辑。
同时拥有接口和抽象基类的唯一原因是避免强制对象继承基类。所以调用站点应该期待这个接口,但是你可以选择使用基本行为的类从某个地方继承。
(顺便说一句,我什至不会在这里做一个抽象,只是一个带有虚拟方法的可继承类,继承和组合都可以使用它)
当然,只有当您实际上可以编写执行所需序列化的通用代码时,基本抽象类才有意义。在您提供的示例中,SharkBiteSerializer 与 SharkBite 耦合,因此它不是一个好的基类(您不能与其他类共享)。
推荐阅读
- python - 为什么在我更改了我的 Wordpress 主题和域后 xmlrpc 不起作用?
- java - Kafka 重试配置和性能影响
- python - Sendgird Web API python 示例代码不起作用:401 未授权
- font-awesome - 如何修复 Whatsapp 字体真棒不起作用
- php - mySQL:在 PDO 中动态插入一行
- r - 如何在 R 中沿 ggplot 的轴添加自定义着色器?
- git - 如何在 Azure DevOps 中分支合并/关闭后执行操作
- python - 如何将带有数字的字符串转换为数组?
- json - 发生飞镖异常。_CastError(类型'String'不是类型转换中类型'int'的子类型)错误
- assembly - MIPS-32 操作码格式:大写还是小写?