使用通用类型构造对象?

时间:2016-03-16 14:58:53

标签: generics typescript

我正在使用公共RESTful API,它返回包含指向更多信息的URL字段的对象。我想将这些字段包装在一个指示它们指向的对象中,所以我创建了一个像这样的对象:

class APIResource<T> {
    url: string;
}

T描述url应返回的内容。就打字稿而言,T实际上甚至没有被对象使用,但它帮助我知道我应该期待什么。我正在使用Angular2,但请注意我正在尝试做的不是Angular特定的。我有一个API类,我可以在其中请求URL并提供一个类型将其转换为:

class Person {
  name: string;
  age: number;

  constructor(obj: any) {
    // all Constructable<T> classes will
    // have a constructor that looks
    // exactly like this
    Object.assign(this, obj);
  }

  print() {
    console.log(this.name + ' is ' + this.age);
  }
}

interface Constructable<T> {
  new (obj: any): T;
}

public get<T>(url: string, type: Constructable<T>): Observable<T> {
  return this.http.get(url).map(result => {
    return new type(result.json());
  });
}

基本上,我发一个ajax请求从API获取json,我将它传递给我的类型的构造函数并返回该对象。它会像这样使用:

var person: Person;
api.get('/person/1/', Person).subscribe(p => person = p);

如果我有构造函数要传入,这是有效的,但我想要做的是写一个getResource<T>(resource: APIResource<T>): Observable<T>函数,这样我就可以这样做:

var res: APIResource<Person> = { url: '/person/1/' };
var person: Person;
api.getResource(res).subscribe(p => person = p);

但到目前为止没有任何效果。现在我无法从T中获取APIResource类型来使用我当前的方法。我试过修改APIResource:

class APIResource<T> {
  url: string;

  getType(): T {
    return T;
  }
}

或者

class APIResource<T extends Constructable<T>> {
  url: string;

  fetch(): T {
    // just a test to see if tsc complains
    // which it does with "cannot find name T"
    var obj: any = {};
    return new T(obj);
  }
}

但它们都不起作用。我知道一旦它是javascript,类型信息就消失了。而不是类型移动,我认为我能够使用移动构造函数,但我不认为我可以使用泛型。

无论如何使用泛型来完成这项工作吗?

我正在使用typescript 1.8.7

1 个答案:

答案 0 :(得分:2)

这是在TypeScript中使用泛型来保持强类型的方法:

interface Constructable<T> {
    ctor: new (...args) => T;
    url: string;
}

class APIResource<T> implements Constructable<T> {
    constructor(
        public ctor: new (...args) => T,
        public url: string
    ) {}
}

class Person {
    name: string;
    age: number;

    constructor(obj: any) {
        //Object.assign(this, obj); // requires ES6
        for (var key in obj) this[key] = obj[key];
    }

    print() {
        console.log(`${this.name} is ${this.age}`);
    }
}

function getResource<T>(apiResource: APIResource<T>): T {
    console.log(`resource url: "${apiResource.url}"`);

    // pretend this came back from an ajax request    
    let obj: any = { name: 'Arthur', age: 42 };
    console.log(`obj instanceof Person? ${obj instanceof Person}`); // false

    return new apiResource.ctor(obj);
}

const res = new APIResource(Person, '/person/1/');
let person = getResource(res);

console.log(`person instanceof Person? ${person instanceof Person}`); // true
person.print();

strong typing is preserved to enable intellisense

我简化了代码,以便它可以在单个自包含的TypeScript文件中运行,以显示这个想法,而不需要Angular 2和rxjs依赖项。但是,将其扩展为Angular 2中的http调用是直截了当的(为清晰起见,将其拆分为多个映射):

getResource<T>(apiResource: APIResource<T>): Observable<T> {
    return this.http.get(apiResource.url)
        .map(result => result.json())
        .map(obj => new apiResource.ctor(obj));
}

然后你可以像往常一样通过subscribe消费强类型的结果。