如何重写不提供签名匹配项的通用构造函数的定义

时间:2018-06-28 19:48:10

标签: javascript typescript generics constructor interface

我正在编写一个库,以实现JavaScript中可迭代类型的延迟评估查询。我知道已经有一些实现此目的的选项,但是我的方法采用了截然不同的方法来公开扩展方法。

我在应对特定的设计挑战时遇到困难,并且想知道是否存在一些我没有想到的解决方法。

我的Enumerable<T>类定义及其接口以及一些帮助程序如下:

// Iterator<T> defined in lib.es2015.iterable.d.ts
interface IteratorFunction<T> {
  (this: IEnumerable<T> | void): Iterator<T>
}

interface CompareFunction<T> {
  (a: T, b: T): number
}

// inspired by pattern for ArrayConstructor in lib.es5.d.ts
interface IEnumerableConstructor {
  new <T>(iterator: IteratorFunction<T>): IEnumerable<T>
  new <T>(iterator: IteratorFunction<T>, compare: null): IEnumerable<T>
  new <T>(iterator: IteratorFunction<T>, compare: CompareFunction<T>): IOrderedEnumerable<T>

  // static methods...
}

// Symbol.compare defined in separate file as a declaration merge to SymbolConstructor
interface IOrderedEnumerable<T> extends IEnumerable<T> {
  readonly [Symbol.compare]: CompareFunction<T>

  // overrides for thenBy()...
}

// IterableIterator<T> defined in lib.es2015.iterable.d.ts
interface IEnumerable<T> extends IterableIterator<T> {
  // member methods...
}

class Enumerable<T> implements IEnumerableConstructor {
  readonly [Symbol.iterator]: IteratorFunction<T>
  readonly [Symbol.compare]: (null | CompareFunction<T>)

  constructor (iterator: IteratorFunction<T>)
  constructor (iterator: IteratorFunction<T>, compare: (null | CompareFunction<T>) = null) {
    this[Symbol.iterator] = iterator
    this[Symbol.compare] = compare
  }
}

问题是

constructor (iterator: IteratorFunction<T>)

是指假设的定义

new (iterator: IteratorFunction<T>): IEnumerable<T>

与提供的定义相反

new <T>(iterator: IteratorFunction<T>): IEnumerable<T>

,以此类推,IEnumerable<T>中的其他重载声明,向我提供错误信息

  

类型'Enumerable<T>'与签名'new <T>(iterator: IteratorFunction<T>): IEnumerable<T>'不匹配。

我考虑过也将IEnumerableConstructor类也更改为通用类,但是后来在尝试执行此操作时遇到了一个问题:

declare global {
  // ...

  interface ArrayConstructor extends IEnumerableConstructor/*<T>*/ { } // illegal!
  interface MapConstructor extends IEnumerableConstructor { }
  interface SetConstructor extends IEnumerableConstructor { }
  interface StringConstructor extends IEnumerableConstructor { }
  interface TypedArrayConstructor extends IEnumerableConstructor { }

  interface Array<T> extends IEnumerable<T> { }
  interface Map<K, V> extends IEnumerable<[K, V]> { }
  interface Set<T> extends IEnumerable<T> { }
  interface String extends IEnumerable<string> { }
  interface TypedArray extends IEnumerable<number> { }
}

为澄清起见,根据ECMAScript规范,我已经声明TypedArrayInt8Array扩展的类(但TypeScript lib.*.d.ts中未提供其类型)文件,因为它从未被直接使用过,这是可以理解的。

正如我所说,此库实现是将扩展方法公开给JavaScript内置组件的一种截然不同的方法,我知道所有的陷阱,但现在我正在寻找一种方法来提供类Enumerable<T>的构造函数的定义,该定义遵循IEnumerable<T>的通用类型。有什么想法吗?

1 个答案:

答案 0 :(得分:3)

IEnumerableConstructor具有构造签名such interface can not be directly implemented by a class

但是在TypeScript中,不需要将类明确声明为实现某些接口-毕竟,类型系统是结构化的,只要兼容该接口,就可以在需要该接口的地方使用该类。< / p>

据我在您的代码中所了解的,Enumerable<T>实际上必须实现IEnumerable<T>,因为这是声明interface IEnumerableConstructor中的构造签名要产生的类型。因此,它应该具有next()方法,然后,如果您只是省略implements IEnumerableConstructor,它似乎就可以工作(但是如果将其替换为implements IEnumerable,则可以使编译器检查该对象是否会受到损害)类正确实现了该接口):

class Enumerable<T>  implements IEnumerable<T> {
  readonly [Symbol.iterator]: IteratorFunction<T>
  readonly compare: (null | CompareFunction<T>)

  constructor (iterator: IteratorFunction<T>)
  constructor (iterator: IteratorFunction<T>, compare?: (null | CompareFunction<T>)) {
    this[Symbol.iterator] = iterator
    this.compare = compare
    }

    next(): IteratorResult<T> {
        return undefined;
    }
}

const C: IEnumerableConstructor = Enumerable;

type A = { a: string };
let a: A[];

const c = new C(a.values); // inferred as const c: IEnumerable<A>