普通对象与模型对象的类实例

时间:2018-08-08 08:40:55

标签: javascript angular typescript prototypal-inheritance

在Angular / TypeScript中创建模型对象的最佳实践是什么

  1. 我应该使用带有对象符号的类型注释(对象是Object的普通实例)吗?例如。 let m: MyModel = { name: 'foo' }

  2. 我应该使用new运算符(对象是各个原型的实例)吗?

  3. 这两种方法应该混合使用吗? (例如,普通对象,当从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最佳实践,因为我认为这是必须从项目开始就做出的核心设计决策,并且这些方法不应无故无故地混在一起。也许答案只是一个简单的“没有官方建议可做出决定”。

2 个答案:

答案 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。

这是否适合您的用例,实际上取决于您执行多少序列化/反序列化。