Angular 2 Typescript:是否可以将接口作为参数传递给函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40285658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angular 2 Typescript: Is it possible to pass an interface as a parameter into a function?
提问by Ole Spaarmann
I have the following problem: I pull data from a JSON API. I have at the moment a service for every data model (eg. articles, users, etc) and a model class for every data model. But this is insane and not really maintainable. So I would like to refactor so that I have an interface and a model class for every data model and one unified DataAPIService
.
我有以下问题:我从 JSON API 中提取数据。目前,我为每个数据模型(例如文章、用户等)提供了一个服务,并为每个数据模型提供了一个模型类。但这很疯狂,而且无法真正维护。所以我想重构,以便为每个数据模型和一个统一的DataAPIService
.
The problem is, that the functions in DataAPIService
that query the API should not return JSON but objects or collections of objects of the type that has been queried. So I need a way to pass the interface or type into the query method of the service to then initialize a new object of this type.
问题是,该DataAPIService
查询 API 中的函数不应返回 JSON,而是已查询类型的对象或对象集合。所以我需要一种方法将接口或类型传递到服务的查询方法中,然后初始化这个类型的新对象。
Is this possible? Do I make sense? Here is some code to help understand what I mean and show my current progress.
这可能吗?我说得通吗?这里有一些代码可以帮助理解我的意思并显示我当前的进度。
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import 'rxjs/add/operator/map';
import { Config } from '../config/env.config';
@Injectable()
export class DataAPIService {
constructor(
private authHttp: AuthHttp
) {}
// This function will be called to retrieve data (for example from a Component).
// I want to pass in the object type or interface so that I only have one
// getIndex() function and not one for every data type.
getIndex(page:number = 1, object_name:string, object_type) {
return this.authHttp.get(Config.API_ENDPOINT + '/' + object_name + '?page=' + page)
.map(res => res.json())
.map(res => {
return this.fromJson(res.data, object_type);
});
}
// This function just checks which attributes are present in the object type
// and fills a new object of this type with the values from JSON.
// This is partly pseudo-code since I don't know how to solve this.
fromJson(input_json: any, object_type) {
// The next line is obviously not working. This is what I'm trying to figure out
var object:object_type = new object_type();
var json_attributes = input_json.attributes;
for (var key in json_attributes) {
if (object.hasOwnProperty(key)) {
object[key] = json_attributes[key];
}
}
object.id = input_json.id;
return object;
}
}
采纳答案by Ole Spaarmann
This is how I have solved the whole thing. It was important for me that the resulting object is not a generic object but for example of the type Post. I also wanted to use interfaces and I wanted the initialization of the object to be easy.
这就是我解决整个问题的方式。对我来说,结果对象不是通用对象,而是例如 Post 类型的对象,这一点很重要。我还想使用接口,并且我希望对象的初始化很容易。
First, I have a base class from which all data models inherit.
首先,我有一个所有数据模型都继承自的基类。
base-model.model.ts
基础模型.model.ts
import * as _ from 'lodash';
export class BaseModel {
public id: string;
[key: string]: any;
constructor(private data?: any) {
// This basically does the initialization from a variable json object.
// I later on just pass the json data into the constructor.
if (data) {
this.id = data.id;
_.extend(this, data.attributes);
}
}
}
Now the actual model that inherits from the Base Model:
现在从基本模型继承的实际模型:
member.model.ts
成员.model.ts
// The actual model. It has an interface and extends the base class
// (so that the main logic is just in one place - DRY)
import { BaseModel } from './base-model.model';
interface MemberInterface {
email:string;
name:string;
}
export class Member extends BaseModel implements MemberInterface {
email:string;
name:string;
constructor(data?: any) {
super(data);
}
}
Let's use it. With a Service that pulls data from an API
让我们使用它。使用从 API 中提取数据的服务
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import 'rxjs/add/operator/map';
import { Config } from '../config/env.config';
@Injectable()
export class MemberService {
constructor(public authHttp: AuthHttp) {}
// Calls the API and returns the result.
// authHttp works very similar to http. Just with added JWT protection
// check it out on GitHub: angular2-jwt
getIndex(page:number = 1):any {
let url = [Config.API_ENDPOINT, 'members', '?page='].join('/');
return this.authHttp.get(url + page)
.map(res => res.json())
.map(res => {
return res;
});
}
// Simpler example when just getting one entry
getOne(id: string):any {
let url = [Config.API_ENDPOINT, 'members', id].join('/');
return this.authHttp.get(url)
.map(res => res.json())
.map(res => {
return res;
});
}
}
And finally let's use the Model class and the Service together
最后让我们一起使用 Model 类和 Service
import { Component, OnInit } from '@angular/core';
import { MemberService } from '../shared/index';
import { Member } from '../shared/models/member.model';
@Component({
moduleId: module.id,
selector: 'app-member-list',
templateUrl: 'member-list.component.html',
styleUrls: ['member-list.component.css']
})
export class MemberListComponent implements OnInit {
private members: Array<Member>;
private member: Member;
constructor(private memberService: MemberService) {
this.members = [];
this.member = new Member();
}
ngOnInit():any {
// Pull the list on initialization
this.getIndex(1);
}
// For the index
getIndex(page:number = 1):Array<Member> {
this.memberService.getIndex(page).subscribe(
res => {
this.members = [];
for(let i = 0; i < res.data.length; i++) {
let member = new Member(res.data[i]);
this.members.push(member);
}
},
err => console.log(err)
);
}
// Simpler version with just one entry
getOne():any {
this.memberService.getIndex(page).subscribe(
res => {
this.member = new Member(res.data.attributes);
},
err => console.log(err)
);
}
}
回答by KwintenP
What you could do is work with generics (if you do not know what these are, I recommend googling this).
您可以做的是使用泛型(如果您不知道这些是什么,我建议您使用谷歌搜索)。
@Injectable()
export class DataAPIService {
constructor(
private authHttp: AuthHttp
) {}
// This function will be called to retrieve data (for example from a Component).
// I want to pass in the object type or interface so that I only have one
// getIndex() function and not one for every data type.
getIndex<T>(page:number = 1, object_name:string): Observable<T> {
return this.authHttp.get(Config.API_ENDPOINT + '/' + object_name + '?page=' + page)
.map(res => res.json());
}
By just adding the T generic to your method, you can define the return type to be an Observable of values with type T. The res.json() will just create an object and if it is returned to the caller of this method, he'll just see an observable of values with type T. No need to write such a specific parsing function to an interface.
通过将 T 泛型添加到您的方法中,您可以将返回类型定义为类型为 T 的值的 Observable。 res.json() 将只创建一个对象,如果它返回给此方法的调用者,他将只看到类型为 T 的值的 observable。无需为接口编写这样一个特定的解析函数。