打字稿中的默认通用类型

时间:2018-07-04 13:39:13

标签: typescript protractor

所以我有一个量角器列表页面对象。该对象默认为返回ElementFinder实例,但可以自定义为返回ComplexType实例,如下所示:

class ComplexType {
  foo = 'foo';

  constructor(public element: ElementFinder) {}
}

const list = new List<ComplexType>({ element: ..., type: ComplexType});
expect(list.get(0).foo).toBe('foo');

这是页面对象定义:

import { ElementFinder } from 'protractor';

export interface ListItem<T> {
  new(element: ElementFinder): T;
}

export interface ListOptions<T> {
  element: ElementFinder;
  selector?: string;
  type?: ListItem<T>;
}

export class List<T = ElementFinder> {
  public readonly element: ElementFinder;
  private selector: string;
  private type: (element: ElementFinder) => T;

  get items() {
    return this.element.$$(this.selector);
  }

  get length() {
    return this.items.count();
  }

  constructor(options: ListOptions<T> | ElementFinder, selector?: string) {
    if (options instanceof ElementFinder) {
      this.element = options;
      this.selector = selector || 'li';
      this.type = (element: ElementFinder) => element;
    } else {
      this.element = options.element;
      this.selector = options.selector || 'li';
      this.type = (element: ElementFinder) => new options.type(element);
    }
  }

  get(index: number): T {
    return this.type(this.items.get(index));
  }
}

我遇到的问题是打字稿无法理解T有时是ElementFinder。因此,当我返回ElementFinder的实例时,它抱怨Element finder与T不匹配。

我很困惑。当然可以吗?

1 个答案:

答案 0 :(得分:1)

问题在于,在该类中,T并不确定是特定的(即使您进行了某些检查,编译器也不会以任何方式缩小T的范围)。这意味着所有分配必须适用于任何可能的T,并且由于this.type = (element: ElementFinder) => element;不适用于任何T,因此会产生错误。

快速而肮脏的解决方法是使用类型断言,如果我们确定这是一个误报的话:

this.type = (element: ElementFinder) => element as any;

更优雅的解决方案是将type更改为必填,并传递适当的函数:

class ComplexType {
    foo = 'foo';

    constructor(public element: ElementFinder) { }
}

export interface ListOptions<T> {
    element: ElementFinder;
    selector?: string;
    type: (element: ElementFinder) => T;
}

export class List<T = ElementFinder> {
    public readonly element: ElementFinder;
    private selector: string;
    private type: (element: ElementFinder) => T;

    get items() {
        return this.element.$$(this.selector);
    }

    get length() {
        return this.items.count();
    }
    public static default(element: ElementFinder, selector = 'li') :ListOptions<ElementFinder>{
        return {
            element,
            selector,
            type : (e) => e
        }
    }
    constructor(options: ListOptions<T>, selector?: string) {
        this.element = options.element;
        this.selector = options.selector || 'li';
        this.type = options.type;
    }

    get(index: number): T {
        return this.type(this.items.get(index));
    }
}
let element!: ElementFinder;

// complex usage
const list1 = new List<ComplexType>({ element, type: e=> new ComplexType(e) });
//default usage
const list2 = new List(List.default(element));