我正在开发一个庞大的Angular 2项目,该项目涉及从noSQL数据库中提取几种不同类型的对象。这些对象用简单的模型类表示。我使用服务从数据库中提取信息并实例化传递给组件的对象,就像英雄教程的工作方式一样。
我遇到的问题是,对于每种类型的对象,我都有一个与其他对象略有不同的服务 - 主要区别是我需要服务来实例化并返回不同类型的对象。为了整合这些服务,我尝试将对象类型从组件传递到服务将进行数据库调用的服务,并实例化传递类型的对象。
看起来像这样:
在组件中:
import { Market } from './market'; // Market is just a simple model class
/* clipped stuff */
ngOnInit(){
this.route.params.forEach(params => {
this.nodeID = params['id'];
this.nodeService.getNodeOfType(Market, this.nodeID)
.subscribe(market => this.market = market)
});
}
服务:
export class NodeService {
getNodeOfType(type, nodeID: number) {
return this.neo4j.readProperties(nodeID)
.map(properties => new type(nodeID, properties));
}
}
我有两个问题:
当我将类类型作为参数传递时,如何在函数参数中输入?
我没有太多的打字稿经验,也没有在文档中看到过类似的东西,这让我怀疑它可能是某种形式的反模式。这是一个糟糕的设计实践,出于某种原因,我稍后会发现?
答案 0 :(得分:2)
一个很大的反模式突出:你没有在getNodeOfType()
方法上声明一个返回类型,从而失去了所有类型的安全性。需要让方法根据某个输入参数返回不同的类型是generic方法的经典用例 - Typescript does support。
以下是在您的情况下实施它的一种可能方式:
getNodeOfType<T>(factory: (input:any, nodeId:number)=>T , nodeID: number): T {
return this.neo4j.readProperties(nodeID)
.map(properties => factory(properties, nodeId));
}
首先,请注意,不是传入一个类型,而是传入一个函数,该函数在给定输入的情况下返回所需类型的对象(T
)。在这里,该输入是any
类型,但如果您知道某些基本类型将始终返回this.neo4j.readProperties()
,则最好用该类型替换any
。
更重要的是,请注意您的函数现在返回T
类型,其中T
根据您传递的工厂函数的返回类型即时确定,因此您不必失去类型安全。
回应你的评论:
似乎没有办法避免为我想要使用的每种类型的对象创建工厂函数。如果我有很多不同类型的话,我仍然会得到相当数量的样板,对吗?
不一定 - 当我使用这种模式时,我传入的工厂函数几乎总是简单的匿名函数。考虑一下这个简单的例子:
class Foo{
constructor(input: any, id:number) {/*constructor stuff*/ }
}
//elsewhere
foo: Foo = myService.getNodeOfType((props, id)=>new Foo(props, id), 1234);
使用service方法获取工厂函数而不是类型的实用程序是,它使您可以更灵活地将检索到的数据映射到所需的结果类型(例如,如果您希望它返回接口或构造函数的类)具有不同的签名,或者如果您想在将其传递给构造函数之前进行输入验证)。
我之前应该注意到,如果您知道自己不需要这种灵活性并且总是将输入传递给具有相同签名的构造函数,那么您可以执行以下操作实现你最初的目标。
getNodeOfType<T>(Type: new(input:any, id:number)=>T , nodeID: number): T {
return this.neo4j.readProperties(nodeID)
.map(properties => new Type(properties, id));
}
//usage (same Foo class as above)
foo: Foo = myService.getNodeOfType(Foo, 1234);
注意第一个参数的类型 - 在你将其分解之前看起来很尴尬:它期望一些构造函数需要两个any
和一个number
作为args并返回一个类型的实例T.
答案 1 :(得分:0)
已编辑(我无法发表评论):
我认为将仿制药用于工厂是一个坏主意。正是这个用例存在抽象类和接口。检查此示例 https://gist.github.com/jordic/589ac6dbc2e0badb5ce29e3e112b47b5
<强>原始强>
为什么不创建一个抽象类,并将该函数的返回类型标记为此抽象类:(对于接口相同)
export abstract class Modeled {}
getNodeOfType(type:ValidModeled, nodeID: number):Modeled
此外,您可以为参数使用联合类型:
https://www.typescriptlang.org/docs/handbook/advanced-types.html
type ValidModeled = "Class1" | "Class2" | "Class3"
对于你的第二个问题:这不是一个坏习惯,你的getNodeOfType是一个工厂函数。