何时在TypeScript中使用Generic Class?

时间:2018-03-22 00:47:24

标签: typescript typescript2.0

文档中有一个示例,但目前尚不清楚如何在实践中使用:

class Result<T> {
 constructor(public wasSuccessful: boolean, public error: T) {
 }
 public clone(): Result<T> {
 ...
 }
}
let r1 = new Result(false, 'error: 42');

2 个答案:

答案 0 :(得分:0)

例如,您可能有抽象类和集合,它正在使用该类的T子节点。

abstract class BaseModel<T extends BaseModel<{}>>{
  abstract get(): any;
  abstract create(): any;
  abstract etc(): this;
}

class ProductModel extends BaseModel<ProductModel>{
  etc(){
    return this;
  }

  get(){
    console.log('Got somehing for product model');    
  }

  create(){
    console.log('Created something');
  }
}

class Service<T extends BaseModel<{}>>{

  constructor(private model: T){

  }

  getSomething(){
    return this.model.get();
  }
}

let service = new Service(new ProductModel());

service.getSomething();

更新:正如您所问的那样

abstract class BaseItem{

}

class Item extends BaseItem{

}

class OpenableItem extends BaseItem{

}

class MultiLineItem extends BaseItem{

}

class Menu<T extends BaseItem>{
  private items: T[];  
}

可以使用接口Item并为每个项执行单独的类,但如果有相同的函数,则使用抽象类可能更好。

答案 1 :(得分:0)

这是一个相当抽象的问题,如果不了解您在编程和一般面向对象概念方面的经验水平,很难适当地回答。

TypeScript中存在的最简单有用的泛型类型以及具有泛型类型的任何其他语言也许是一般类型的数组或列表。

您可以在阵列上执行少量操作,而不知道它包含的确切类型。例如Array.prototype.map的简单示例:

function map<T, R>(array: Array<T>, callback: (t: T) => R): Array<R> {
    const ret: Array<R> = [];

    for (let i = 0; i < array.length; i++) {

        // We can guarantee that the result of calling `callback`
        // will be an R, whatever R may be
        const cbResult: R = callback(array[i]);

        // Because we know it's an R, we can safely
        // push it into the array of R!
        ret.push(cbResult);
    }

    // Now the caller of the function has a guarantee that what
    // is returned from `map` is an Array of type R where R is
    // the result type of the callback
    return ret;
}

class Foo {
    constructor(public bar: string) { }
}

const stringArray = ['a', 'b', 'c'];
const numberArray = [1, 2, 3];
const fooArray = [new Foo('hello'), new Foo('friend')];

// By using generic type parameters, we can guarantee the
// type safety of the callback functions
const numberArrayAsStrings: Array<string> = map<number, string>(numberArray, (num) => num.toString());

// Each one of these callbacks knows for a fact that the type T
// will be passed in and that they've promised to return the type R
const stringArrayAsUppercase: Array<string> = map<string, string>(stringArray, (letter) => letter.toUpperCase());

// When we do that, we can write functions like map that do
// complex operations on multiple different types of objects
const fooArrayAsBars: Array<string> = map<Foo, string>(fooArray, (foo) => foo.bar);

因此,当您调用此Array.prototype.map的实现时,map函数的实现不必知道数组包含的内容,但您作为消费者可以获得知道该数据的所有好处。 T类型的元素将被传递到您的回调中,并且作为调用R的结果,将返回类型为map的数组。

这意味着,在最简单的用法中,泛型对API设计者很有用,它在通常包含泛型类型的数据模型上提供一些抽象操作。然后它们对API的使用者有用,因为在您仍然在客户端代码中维护类型安全的同时,API提供的任何操作都是可能的。

使用上面的map函数的示例,您可以看到字面上可以映射任何类型的数组,以使用本身类型安全的回调函数创建结果类型的新类型安全数组。

通常,泛型与接口相结合,为不同类型的对象提供特定的操作。原谅长长的榜样!我不确定是否有一个很好的方法来提供一个不太长的有用的例子。

interface IEquatable<T> {
    equals: (other: T) => boolean;
}

interface IComparable<T> extends IEquatable<T> {
    compare: (left: T, right: T) => 1 | 0 | -1;
}

class Bar implements IEquatable<Bar> {
    constructor(public bang: number) { }

    equals(right: Bar) {
        return this.bang === right.bang;
    }
}

class Baz implements IComparable<Baz> {
    constructor(public zim: number) { }

    compare(left: Baz, right: Baz) {
        if (left.zim > right.zim) return 1;
        else if (left.zim === right.zim) return 0;
        else return -1;
    }

    equals(other: Baz) {
        return this.compare(this, other) === 0;
    }
}

/**
 * Sort function that would rely on the result of calling compare
 * each element in the array to sort them according to the comparison
 */
function sort<T extends IComparable<T>>(array: Array<T>): Array<T> {
    // Saving space by not implementing this
    return array;
}

/**
 * Determines whether the arrays have the same contents by sorting
 * the arrays and then calling the arrayEqual function to see if the
 * sorted arrays are equal
 */
function arraySameContents<T extends IComparable<T>>(left: Array<T>, right: Array<T>): boolean {
    if (left.length !== right.length) return false;

    const sortedLeft = sort(left);
    const sortedRight = sort(right);

    return arrayEqual(sortedLeft, sortedRight);
}

/**
 * Compares each element in the left array to the element at the same
 * index in the right array, returning true if all elements are equal
 * and false otherwise
 */
function arrayEqual<T extends IEquatable<T>>(left: Array<T>, right: Array<T>): boolean {
    if (left.length !== right.length) return false;

    for (let i = 0; i < left.length; i++) {
        if (!left[i].equals(right[i])) {
            return false;
        }
    }

    return true;
}

正如您所看到的,通过在我们的数组比较函数和接口中使用泛型类型参数,我们可以保证可以比较的数组类型,而不需要知道关于被比较的对象的单个事物而不是它们实现compareequals功能!这是通用编程功能的一个相对简单的例子。 RxJS框架是仿制药真正发挥作用的一个很好的例子。

对于一个外卖练习:你可以在一个声明看起来大致如此的类上实现IEquatableIComparable接口吗?

class EquatableArray<T> extends Array<T> implements IEquatable<T> {

}

在开始为此编写任何代码之前需要考虑的几个问题:类型参数T是否需要有任何限制?为了能够为equals编写ComparableArray函数,我们需要知道有关T的任何额外信息吗?如果我们尝试使用之前编写的函数arrayEqualarraySameContents

,该怎么办?

希望这有帮助!祝好运!