类型安全的原型修补程序

时间:2019-04-04 11:07:42

标签: typescript typescript3.0

说我在模块src/Container中有以下接口

export abstract class Container<T> implements Iterable<T> {
  public abstract [Symbol.iterator](): IterableIterator<T>
}   

现在我想从另一个文件src/extensions/transformSomething扩展该类

import { Container } from '../Container'
declare module '../Enumerable' {
  interface Container<T> {
    transformSomething<TOut>(this: Container<T>, selector: (x : T) => TOut) : Container<TOut>
  }
}

然后我定义此扩展功能的实现

function transformSomething<T, TOut>(
  this: Container<T>,
  selector: (x : T) => TOut
): Container<TOut> {
  //...
  const returnValue:SomeContainerType<TOut> = //...
  return returnValue
}

我现在可以轻松地将其附加到Container<T>

Container.prototype.transformSomething = transformSomething

获奖。

现在,说我已经有一个采用以下形式的不同,功能更丰富,原型较少的实现:

(...args: any[]) => (src: any) => any

例如:

export const transformSomethingF = <T, TOut>(selector: (x : T) => TOut) => (src: Container<T>): Container<TOut> => {
    //...
    const returnValue:SomeContainerType<TOut> = //...
    return returnValue
}

我可以编写一个将其转换为基于this的函数的方法:

export const convertForPrototype =
  <TIn, TOut, U extends any[], T extends (...args: U) => (src: TIn) => TOut>
    (func: T): (this: TIn, ...args: U) => TOut => {
    return function (this: TIn, ...args: U) {
      return func(...args)(this);
    };
  };

现在,将我们的transformSomethingF转换为可在原型中运行的函数非常简单:

Enumerable.prototype.select = convertForPrototype(select)

那么,我们可以对上面的接口扩展做同样的事情吗?我们可以改变

import { Container } from '../Container'
declare module '../Enumerable' {
  interface Container<T> {
    transformSomething<TOut>(this: Container<T>, selector: (x : T) => TOut) : Container<TOut>
  }
}

进入

import { Container } from '../Container'
declare module '../Enumerable' {
  interface Container<T> {
    transformSomething: PrototypeRepresentation<typeof transformSomethingF>
  }
}

???

我们可以通过定义以下类型来做到这一点:

export type PrototypeRepresentation<TSrc,T extends (...args: any[]) => (src: any) => any> =
    T extends (...args: infer P) => (src: TSrc) => infer TOut ?
    (this: TSrc, ...arg: P) => TOut : never;

但是当我尝试这样做时,尝试实际使用Container.transformSomething

const someContainer : Container<number> = //...
const result = someContainer.transformSomething(x => x * 2)

我看到以下问题:

  

(参数)x:任意

     

无法调用类型缺少调用签名的表达式。类型“从不”没有兼容的呼叫签名。ts(2349)

     

参数'x'隐式具有'any'类型。ts(7006)

是否有办法使它正常工作?

0 个答案:

没有答案