net*_*djw 2 typescript typescript-generics
我有一个可以处理本地存储中的数据的服务,还有另一个可以处理远程 API 查询的服务。当我定义数据模型时,我会给出模型是否使用本地存储。
现在我尝试构建一个代理服务,它可以根据模型定义决定从本地存储还是从远程 API 请求数据。
现在我有这个代码:
export class ProxyService<T> {
public type: new () => T;
private emptyModel: T = new this.type();
private localstorageService: LocalstorageDataService<T> =
new LocalstorageDataService(this.type, this.httpClient);
private remoteDataService: RemoteDataService<T> = new RemoteDataService(this.type, this.httpClient);
constructor(
private httpClient: HttpClient,
) { }
getOne(id: number): Observable<T> {
if (this.emptyModel.use_localstorage) {
return of(this.localstorageService.findById(id));
} else {
return this.remoteDataService.getOne(id).pipe(map((result: T) => result));
}
}
// other proxy functions are here...
}
Run Code Online (Sandbox Code Playgroud)
但我收到这个错误:
Error: Uncaught (in promise): TypeError: this.type is not a constructor
TypeError: this.type is not a constructor
at new ProxyService (proxy.service.ts:19)
Run Code Online (Sandbox Code Playgroud)
RemoteDataService 和 LocalstorageDataService 都使用模型,因此 ProxyService 是我定义模型的最佳位置。因为整个项目中多个地方都用到了ProxyService,而且有多个模型。
顺便说一句,第 19 行是上面的代码:
Error: Uncaught (in promise): TypeError: this.type is not a constructor
TypeError: this.type is not a constructor
at new ProxyService (proxy.service.ts:19)
Run Code Online (Sandbox Code Playgroud)
我尝试在构造函数中修复它:
private emptyModel: T = new this.type();
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,我需要在调用 ProxyService 时定义一个模型,并且我不想在使用 ProxyService 时每次都定义一个模型。在这种情况下,我无法像下面的代码一样使用 ProxyService,但这就是我想要使用的方式:
export class ProxyService<T> {
// ...
constructor(
public type: new () => T;
private httpClient: HttpClient,
) { }
// ...
Run Code Online (Sandbox Code Playgroud)
知道如何解决这个问题吗?
您似乎将 TypeScript 类型注释与运行时 JavaScript 具体实现混淆了。
在 TypeScript 中你不能执行 a new T(),因为 T 只是一个类型注释并且没有具体的实现。
你的type领域undefined自然private emptyModel: T = new this.type();会失败。
我认为你找到了正确的解决方案。
不是将 Proxy 类创建为单例,而是创建为类。
然后创建一个公开用于返回实例的工厂方法的服务。
export class ProxyFactoryService {
constructor(private httpClient: HttpClient) { }
proxyFactory<T>(newable: new() => T) {
const instance = new newable();
return new ProxyService<T>(this.httpClient, instance);
}
}
Run Code Online (Sandbox Code Playgroud)
那么你的 ProxyService 可能看起来像这样:
export class ProxyService<T> {
private localstorageService: LocalstorageDataService<T>;
private remoteDataService: RemoteDataService<T>;
constructor(private httpClient: HttpClient, private instance: T) {
this.localstorageService = new LocalstorageDataService(this.instance, this.httpClient);
this.remoteDataService = new RemoteDataService(this.instance, this.httpClient)
}
getOne(id: number): Observable<T> {
if (this.instance.use_localstorage) { // we probably want to make sure T extends {use_localStorage: boolean}
return of(this.localstorageService.findById(id));
} else {
return this.remoteDataService.getOne(id).pipe(map((result: T) => result));
}
}
// other proxy functions are here...
}
Run Code Online (Sandbox Code Playgroud)
要使用这个
constructor(private proxyService: ProxyService) {}
ngOnInit() {
const proxy = this.proxyService.proxyFactory(SomeClass);
proxy.getOne(...);
}
Run Code Online (Sandbox Code Playgroud)
总是问自己一个问题:删除注释后这将如何工作?
当 TypeScript 编译时,所有类型注释都会被删除——这些只是设计时静态类型注释。
另一种思考方式是这种简化——我们将删除泛型并使用具体类型。但本质上这归结为你想要做的事情:
const s: new () => String;
const i = new s();
Run Code Online (Sandbox Code Playgroud)
s调用时返回类型的构造函数也是如此String。这将不起作用,因为s未定义。我们知道它应该是什么类型,但实际上它是未定义的。这一切都是非常做作的——但我希望这会有所帮助。
如果我们查看上面的编译器输出,应该会更清楚为什么这不起作用。
const s;
const i = new s();
Run Code Online (Sandbox Code Playgroud)
幸运的是,在我们真正有机会运行这段代码之前,TypeScript 就会出错。如果您的 IDE 设置正确,您应该在设计时收到大量警告和错误。
我知道这可能会令人沮丧和困惑,尤其是来自 C# 和 Java 等 CIL/CLR 静态类型感知的语言。在 TypeScript 中没有运行时,它只是编译为 JavaScript。
静态类型和类型注释在运行时不可用。