本文介绍了什么是使用类和接口之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这样做有什么区别

export class Comment {
  likes: string;
  comment: string;

  constructor(likes: string, comment: string){
    this.comment = comment;
    this.likes = likes;
  }
}

这个

export interface CommentInterface {
  likes: string;
  comment: string;
}

与声明可观察类型相关

register: Observable<CommentInterface[]> {
    return this.http.get()
}


推荐答案

正如JB Nizet非常正确地指出的那样,HTTP请求产生的反序列化JSON值永远不会是类的实例。

As JB Nizet quite correctly points out, the deserialized JSON values that result from HTTP requests will never be instances of a class.

虽然是双重的TypeScript中构造的角色(见下文)可以使用来描述这些形状响应值,这是一个糟糕的做法,因为响应文本将被反序列化为纯JavaScript对象。

While the dual role (see below) of the class construct in TypeScript makes it possible to use a class to describe the shape of these response values, it is a poor practice because the response text will be deserialized into plain JavaScript objects.

请注意,在整个答案中我不使用类型 Comment.likes 因为在问题中你将它作为字符串,但感觉就像 number 给我,所以我把它留给了读者。

Note that throughout this answer I do not use a type for Comment.likes because in the question you have it as a string, but it feels like a number to me, so I leave it up the reader.

JavaScript和TypeScript中的类声明:

在JavaScript中,类声明

In JavaScript, a class declaration

class Comment {
  constructor(likes, comment) {
    this.likes = likes;
    this.comment = comment;
  }
}

创建一个可以使用<$ c实例化的值$ c> new 充当本质的工厂。

creates a value that can be instantiated using new to act as what is essentially a factory.

在TypeScript中,类声明创建两个事情。

In TypeScript, a class declaration creates two things.

第一个是与上述完全相同的JavaScript类
第二个是类型,描述通过编写创建的实例的结构

The first is the exact same JavaScript class value described above.The second is a type that describes the structure of the instances created by writing

new Comment(4, 'I love your essays')

第二个神器,类型,然后可以用作类型注释,例如在你的例子中

That second artifact, the type, can then be used as a type annotation such as in your example of

register(): Observable<Comment[]> {
    return this.http.get()
}

这表示 register 返回 <$ c的数组的 Observable $ c> class instances。

which says that register returns an Observable of Arrays of Comment class instances.

现在假设您的HTTP请求返回以下JSON

Now imagine your HTTP request returns the following JSON

[
  {
    "likes": 4,
    "comment": "I love you oh so very much"
  },
  {
    "likes": 1,
    "comment": "I lust after that feeling of approval that only likes can bring"
  }
]

然而方法声明

register(): Observable<Comment[]>;

正确允许来电者写信

register().subscribe(comments => {
  for (const comment of comment) {
    if (comment.likes > 0) {
      likedComments.push(comment);
    }
  }
});

这一切都很好,不幸的是它还允许调用者编写像

which is all well and good, it unfortunately also allows callers to write code like

getComments() {
  register().subscribe(comments => {
    this.comments = comments;
  });
}

getTopComment() {
  const [topComment] = this.comments.slice().sort((x, y) => y < x);
  // since there might not be any comments, it is likely that a check will be made here
  if (topComment instanceof Comment) { // always false at runtime
    return topComment;
  }
}

由于评论实际上不是<$ c的实例$ c>评论类上面的检查总是会失败,因此代码中有一个错误。但是,typescript不会捕获错误,因为我们说 comments 是一个 Comment 类的实例数组会使检查有效(回想一下 response.json()返回任何,可以将其转换为任何类型警告所以在编译时一切都很好。

Since comments are not actually instances of the Comment class the above check will always fail and hence there is a bug in the code. However, typescript will not catch the error because we said that comments is an array of instances of the Comment class and that would make the check valid (recall that the response.json() returns any which can be converted to any type without warnings so everything appears fine at compile time.

但是,如果我们已将注释声明为接口

If, however we had declared comment as an interface

interface Comment {
  comment: string;
  likes;
}

然后 getComments 将继续输入check,因为它实际上是正确的代码,但 getTopComment 将在编译时在if语句中引发错误,因为正如许多其他人所指出的那样,接口 ,作为一个仅编译时构造,不能被用作执行 instanceof 检查的构造函数。编译器会告诉我们我们有错误。

then getComments will continue to type check, because it is in fact correct code, but getTopComment will raise an error at compile time in the if statement because, as noted by many others, an interface, being a compile time only construct, can not be used as if it were a constructor to perform an instanceof check. The compiler will tell us we have an error.

备注:

除了所有其他原因外,在我看来,当你有代表,使用类通常是矫枉过正。它创建了一个带有原型的函数,并且有许多我们不太可能不需要或不关心的其他方面。

In addition to all the other reasons given, in my opinion, when you have something that represents plain old data in JavaScript/TypeScript, using a class is usually overkill. It creates a function with a prototype and has a bunch of other aspects that we do not likely need or care about.

它还会抛弃你默认获得的好处你使用对象。这些好处包括用于创建和复制对象的语法糖以及TypeScript对这些对象类型的推断。

It also throws away benefits that you get by default if you use objects. These benefits include syntactic sugar for creating and copying objects and TypeScript's inference of the types of these objects.

考虑

import Comment from 'app/comment';

export default class CommentService {

  async getComments(): Promse<Array<Comment>> {
    const response = await fetch('api/comments', {httpMethod: 'GET'});
    const comments = await response.json();
    return comments as Comment[]; // just being explicit.
  }

  async createComment(comment: Comment): Promise<Comment> {
    const response = await fetch('api/comments', {
      httpMethod: 'POST',
      body: JSON.stringify(comment)
    });
    const result = await response.json();
    return result as Comment; // just being explicit.
  }
}

如果评论是一个界面,我想使用上面的服务来创建评论,我可以按照以下方式进行操作

If Comment is an interface and I want to use the above service to create a comment, I can do it as follows

import CommentService from 'app/comment-service';

export async function createComment(likes, comment: string) {
  const commentService = new CommentService();

  await commentService.createCommnet({comment, likes});
}

如果评论是一个类,我需要引入一些锅炉板,需要导入 评论。当然,这也会增加耦合。

If Comment were a class, I would need to introduce some boiler plate by necessitating the import of Comment. Naturally, this also increases coupling.

import CommentService from 'app/comment-service';
import Comment from 'app/comment';

export async function createComment(likes, comment: string) {
  const commentService = new CommentService();

  const comment = new Comment(likes, comment); // better get the order right

  await commentService.createCommnet(comment);
}

这是两个额外的行,一个涉及依赖另一个模块来创建一个对象。

That is two extra lines, and one involves depending on another module just to create an object.

现在,如果注释是一个接口,但我想要一个复杂的类来进行验证,而不是之前我将它提供给我的服务,我仍然可以使用它。

Now if Comment is an interface, but I want a sophisticated class that does validation and whatnot before I give it to my service, I can still have that as well.

import CommentService from 'app/comment-service';
import Comment from 'app/comment';

// implements is optional and typescript will verify that this class implements Comment
// by looking at the definition of the service method so I could remove it and
// also remove the import statement if I wish
class ValidatedComment implements Comment {
  constructor(public likes, public comment: string) {
    if (Number(likes) < 0 || !Number.isSafeInteger(Number(likes))) {
      throw RangeError('Likes must be a valid number >= 0'
    }
  }
}

export async function createComment(likes, comment: string) {
  const commentService = new CommentService();

  const comment = new ValidatedComment(likes, comment); // better get the order right

  await commentService.createCommnet(comment);
}

简而言之,有很多理由使用接口来描述响应的类型以及与HTTP服务交互的请求w使用TypeScript。

In short there are many reasons to use an interface to describe the type of the responses and also the requests that interact with an HTTP service when using TypeScript.

注意:您还可以使用类型声明,同样安全可靠,但它不那么惯用,并且围绕界面的工具通常会使这种情况更受欢迎。

Note: you can also use a type declaration, which is equally safe and robust but it is less idiomatic and the tooling around interface often makes it preferable for this scenario.

这篇关于什么是使用类和接口之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 17:42