正如JB Nizet非常正确地指出的那样,由HTTP请求产生的反序列化的JSON值永远不会是类的实例。
虽然打字稿的class
构建的双重作用(见下文)使得有可能使用一个class
描述这些响应值的形状,它是一种较差的做法,因为所述响应文本将被反序列化为普通的JavaScript对象。
注意,在整个这个答案,因为在这个问题你有它作为一个string
,但感觉像一个number
给我,所以我把它留给读者,我不使用类型Comment.likes
。
类声明JavaScript和打字稿:
在JavaScript中,类声明
class Comment {
constructor(likes, comment) {
this.likes = likes;
this.comment = comment;
}
}
创建可以使用new
充当什么本质上是一个工厂被实例化的值。
在TypeScript中,类声明创建了两个的东西。
第一个是上面描述的完全相同的JavaScript类值。 第二种是类型描述通过写
new Comment(4, 'I love your essays')
即第二工件,类型,然后可以用作类型注释如在你的
register(): Observable<Comment[]> {
return this.http.get()
}
示例中创建的实例的结构
其中说register
返回Comment
class
实例的数组的Observable
。
现在,想象你的HTTP请求返回以下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);
}
}
});
这是一个好好,不幸的是,它也允许呼叫者编写代码,如
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;
}
}
由于意见实际上不是Comment
类上面的检查总是会失败,因此没有在代码中的错误的实例。然而,typescript不会捕获错误,因为我们说comments
是Comment
类的一个实例的数组,这将使检查有效(回想一下response.json()
返回any
,它可以转换为任何类型而没有警告,因此一切都很正常编译时间。
但是如果我们宣布评论为interface
interface Comment {
comment: string;
likes;
}
然后getComments
将继续类型检查,因为它实际上是正确的代码,但getTopComment
将在编译时产生错误if语句是因为,如前所述b y许多其他人,仅作为编译时构造的interface
不能用作执行instanceof
检查的构造函数。编译器会告诉我们我们有一个错误。
备注:
除了考虑到所有其他的原因,在我看来,当你有一些代表普通的旧数据的JavaScript /打字稿,使用类通常是矫枉过正。它用原型创建了一个函数,并且有一些我们不太需要或关心的其他方面。
它还会抛弃您在使用对象时默认获得的好处。这些好处包括用于创建和复制对象的语法糖以及TypeScript对这些对象类型的推断。
考虑
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.
}
}
如果Comment
是一个接口,我想使用上述服务创建一个评论,我可以做如下
import CommentService from 'app/comment-service';
export async function createComment(likes, comment: string) {
const commentService = new CommentService();
await commentService.createCommnet({comment, likes});
}
如果Comment
是一类,我需要通过Comment
的import
来引入一些锅炉板。当然,这也增加了耦合。
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);
}
这是两个额外的行,一个涉及到另一个模块只是为了创建一个对象。
现在如果Comment
是一个接口,但我希望有一个复杂的类,它验证和诸如此类的东西之前,我把它给我的服务,我仍然可以有这一点。
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);
}
总之有很多理由使用interface
来形容答复以及使用打字稿时与HTTP服务交互的请求的类型。
注:你也可以使用一个type
声明,这同样是安全可靠的,但它是不太习惯和周围interface
模具往往使优选该方案。
http.post()永远不会返回你的类的一个实例。因为所有的http都会将正文解析为JSON,这会生成基本的JavaScript对象。它不会实例化您的类。 –
我觉得这个问题很清楚,不应该关闭。为什么?因为Angular文档中充斥着使用'class'而不是'interface'的例子。在声明XHR响应时,“interface”是正确的选择。 –
我只是用它作为例子。我改变它得到和登录,所以它更有意义 – Anonguy123