代码之家  ›  专栏  ›  技术社区  ›  Patrick Roberts Benjamin Gruenbaum

如何重写不提供匹配签名的泛型构造函数的定义

  •  1
  • Patrick Roberts Benjamin Gruenbaum  · 技术社区  · 6 年前

    我正在编写一个库,以在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> ,向我提供错误

    类型' 可枚举<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> { }
    }
    

    为了澄清,我宣布 TypedArray 作为那个班 Int8Array 依此类推,根据ecmascript规范进行扩展(但typescript中没有提供它的类型 lib.*.d.ts 因为它从不直接使用,这是可以理解的)。

    正如我所说,这个库实现是一种完全不同的方法,它将扩展方法公开给javascript中的内置方法,我知道所有的陷阱,但是现在我正在寻找一种方法来为类提供定义。 可枚举<t> 构造函数,该构造函数尊重 IEnumerable<t> 是的。有什么想法吗?

    1 回复  |  直到 6 年前
        1
  •  3
  •   artem    6 年前

    IEnumerableConstructor 有构造签名, such interface can not be directly implemented by a class 是的。

    但在typescript中,类不需要显式声明为实现某个接口——毕竟,类型系统是结构化的,只要接口兼容,就可以在需要接口的地方使用类。

    根据我对你代码的理解, 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>