在Angular / TypeScript中创建模型对象的最佳实践是什么
我应该使用带有对象符号的类型注释(对象是Object
的普通实例)吗?例如。 let m: MyModel = { name: 'foo' }
我应该使用new
运算符(对象是各个原型的实例)吗?
这两种方法应该混合使用吗? (例如,普通对象,当从HttpClient
收到响应时,但是new MyModel('foobar')
为方便通过将属性作为构造函数参数传递来创建实例)
我是TypeScript的新手,并且像许多其他开发人员一样,我来自Java等流行的面向对象语言。
我了解的一个核心概念是TypeScript中的类型注释在运行时不起作用。它们对于编译器和编辑器的自动完成很重要。基本上,它是ECMAScript,带有编译时类型检查。
当时我还不知道这一点,并希望TypeScript成为某种“客户端Java”,所以我发现了这个Angular错误报告: https://github.com/angular/angular/issues/20770
人们(不了解TypeScript的类型概念)抱怨HttpClient
没有将返回的普通对象转换为他们的模型类类型。其他人为这种行为辩护,并指出JavaScript中没有运行时类型。
这引起了我的问题:实际上,JavaScript中存在运行时类型。您可以使用new
运算符创建原型实例,甚至可以使用instanceof
运算符检查原型的构造函数。
在TypeScript中,您可以通过两种方式创建实例:
1)使用对象符号(如Angular tutorial中所示):
hero: Hero = {
id: 1,
name: 'Windstorm'
};
2)使用new
运算符:
hero: Hero = new Hero();
目前,我正在一个将这两个选项混在一起的项目中。即相同类型的模型对象在某些情况下是Object
的实例,在其他情况下是Hero
的实例。
我希望这会在以后导致问题,因为仅在后一种情况下才调用构造函数。
我对规则/最佳实践的想法是将所有模型实例定义为纯对象,并将构造函数用于依赖注入创建的服务,组件等。结果,我根本不会使用new
运算符。
但是我不确定这是否是一个好主意,我找不到关于它的最佳实践建议。
编辑
亲密选民的重要注意事项:我不是在这里寻找您的个人选择。我宁愿寻找某种正式记录的Angular最佳实践,因为我认为这是必须从项目开始就做出的核心设计决策,并且这些方法不应无故无故地混在一起。也许答案只是一个简单的“没有官方建议可做出决定”。
答案 0 :(得分:1)
我认为,如果需要对对象本身执行复杂的操作,则应该使用new
运算符创建类的新对象。
如果只需要访问属性,则可以使用对象文字来创建新对象。
这具有诸如封装,继承等优点,而来自Java背景的开发人员可以更好地与之联系。
如果您直接分配如下所示的对象,则必须在其中明确设置函数,例如getName
函数。
hero: Hero = {
id: 1,
name: 'Windstorm',
getName: function(){ // returns name }
};
但是,通过使用函数Hero
定义一个类getName
然后创建该类的实例,无论创建实例多少次,您都将自动获得该function
为了更好地理解面向对象的Javascript,您可以点击以下链接:-
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS
关于以下问题,
“人们(不了解TypeScript的类型概念)抱怨 关于HttpClient不能将返回的普通对象转换为其 模型类类型。”
HttpClient仅返回服务器发送的对象,它在JS上将其转换为所需的形式。您始终可以使用其构造函数将响应映射到所需的模型实例,该构造函数的定义如下:-
constructor(hero){
this.id = hero.id;
this.name = hero.name;
}
,您可以映射从服务器返回的数组,如下所示:-
map(hero => new Hero(hero))
答案 1 :(得分:0)
实际上,JavaScript中有运行时类型。
种类。值可以具有某种类型,但是变量和属性没有绑定到它们的类型。也就是说,如果您键入网络请求等。
fetch("someurl").then(res => res.json()) as Promise<IUser>
,并且该网络请求突然返回除用户以外的其他内容,因为在运行时Typescript类型不存在,所以不会失败。但是,可以使用typeguard轻松添加此行为:
function isUser(user: any) : user is IUser {
return Number.isInteger(user.id) && typeof user.name === "string";
}
它允许您在运行时进行类型检查:
const result = await fetch("someurl");
if(!isUser(result)) throw new Error();
// result is definetly a IUser here
我应该使用继承吗?
这是一个偏爱的问题。我曾经写过一个与db紧密配合的应用程序,所以我不得不做大量的序列化/反序列化工作,这让我总是将db的响应包装到类实例中确实使我很烦。因此,对于我的数据库模型,我开始使用以下模式:
type User = {
name: string;
id: number;
};
const User = {
get(id: number): User { /*...*/ }
changeName(user: User, name: string) { /*..*/ }
};
那让我写了:
const admin: User = User.get(123);
我可以简单地在数据库上进行类型转换,而且我俩都具有类型安全性和良好的API。
这是否适合您的用例,实际上取决于您执行多少序列化/反序列化。