Простые объекты VS экземпляры класса для объектов модели

Какова лучшая практика для создания объектов модели в 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):

hero: Hero = {
  id: 1,
  name: 'Windstorm'
};

2) Используйте new -оператором:

hero: Hero = new Hero();

В данный момент я работаю над проектом, в котором смешаны эти два варианта. Т.е. объекты модели одного типа являются экземплярами Object в некоторых случаях и случаях Hero в других случаях.

Я ожидаю, что это приведет к проблемам позже, потому что конструктор вызывается только в последнем случае.

Моя идея для правила / наилучшей практики состояла в том, чтобы определить все экземпляры модели как простые объекты и использовать конструктор для служб, компонентов и т. Д., Которые создаются путем внедрения зависимостей. В результате я бы не стал использовать new оператор на всех.

Однако я не уверен, что это хорошая идея, и я не смог найти рекомендации по этому поводу.

РЕДАКТИРОВАТЬ

Важное примечание для близких избирателей: я не ищу ваше личное мнение здесь. Я скорее ищу какую-нибудь официально документированную лучшую практику Angular, так как считаю, что это принципиальное проектное решение, которое должно быть принято с самого начала проекта, и эти подходы не должны смешиваться случайным образом без конкретной причины. Может быть, ответ прост: "Нет официальной рекомендации, какое решение принять".

2 ответа

На мой взгляд, вы должны использовать 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))

На самом деле есть типы времени выполнения в JavaScript.

Вид. Значение может иметь определенный тип, однако переменные и свойства не имеют привязанных к ним типов. Тем не менее, если вы вводите сетевые запросы и т. Д.

  fetch("someurl").then(res => res.json()) as Promise<IUser>

и этот сетевой запрос неожиданно возвращает что-то еще, кроме пользователя, ничего не произойдет, так как типы Typescript не существуют во время выполнения. Однако это поведение может быть легко добавлено с помощью typeguards:

  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

Должен ли я использовать наследство или нет?

Это вопрос предпочтений. Однажды я написал приложение, которое интенсивно работает с базой данных, поэтому мне пришлось много заниматься сериализацией / десериализацией, и меня очень раздражало то, что я всегда переносил ответ из базы данных в экземпляр класса. Поэтому для моих моделей БД я начал использовать следующий шаблон:

  type User = {
   name: string;
   id: number;
 };

 const User = {
   get(id: number): User { /*...*/ }
   changeName(user: User, name: string) { /*..*/ }
};

Это позволило мне написать:

 const admin: User = User.get(123);

И я мог просто делать Typecast на БД, и у меня обоих была безопасность типов и хорошие API.

Является ли это хорошим шаблоном для вашего сценария использования или нет, на самом деле зависит от того, сколько сериализации / десериализации вы делаете.

Другие вопросы по тегам