所以我有一个量角器列表页面对象。该对象默认为返回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不匹配。
我很困惑。当然可以吗?
答案 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));