【问题标题】:Mimicking Extension Methods in TypeScript模仿 TypeScript 中的扩展方法
【发布时间】:2019-12-24 23:28:59
【问题描述】:

这是一个有趣的挑战。我正在阅读 TypeScript github 中的这个旧问题到 support Extension Methods,其用法类似于 C#。提出了两种主要的方法,一种是添加语法糖来扩展通常不受欢迎的对象原型,另一种是重写调用站点(参见优缺点here)。

我在想也许我们可以用另一种方式来模仿也适用于 JavaScript 的扩展方法。

假设我们有以下函数:

function includesAnyOf<T>(array: T[], ...searchElements: T[]) {
  return searchElements.some(_ => array.includes(_));
}

// example:
const a = [1, 2, 3];
const b = 3;
includesAnyOf(a, b); // true

// as extension method it would look like:
// a.includesAnyOf(b);

现在我想实现函数ext,这样我就可以做到:

ext(a).includesAnyOf(b)

罢工>

ext(a)(_ => _.includesAnyOf(b));

保留参数的所有类型。我想知道这是否可能,但我从这个开始,不知道如何完成它!

function ext(obj: any) {
  return {
    includesAnyOf: (...args: any[]) => includesAnyOf(...???)
  };
}

我认为这是一个有趣的挑战,您认为这可以如何实现,如何概括它,或者您能想到更好的方法吗?

【问题讨论】:

  • 您可能对管道运营商github.com/tc39/proposal-pipeline-operator 感兴趣 - 不过它仍处于早期阶段。
  • 哇,好强大!感谢分享!
  • 我不确定您为什么希望 ext(a) 成为函数而不是扩展对象。也就是说,你为什么要做ext(a)(x =&gt; x.includesAnyOf(1)) 而不仅仅是(ext(a)).includesAnyOf(1)?
  • 顺便说一句,我认为这里的一个令人困惑的问题是,即使使用recent improvements,您也需要比 TS 能够轻松处理的更高阶类型。如果includesAnyOf 专门只处理具体类型number[],您可能会使用通用扩展函数获得良好的强类型。但由于您可能希望它支持泛型 T 的 T[],这是更高阶的,您可能会在其中看到 unknown[] 或其他过早解析的泛型。
  • @jcalz 回答你的第一个问题,我可能想多了!我基本上是在寻找最接近a.includesAnyOf(b) 语法的路径,而无需扩展对象原型或克隆数组。所以如果我们能做到ext(a).includesAnyOf(b)那就更好了。

标签: javascript typescript extension-methods


【解决方案1】:

我发现最简单的方法是使用lodash.partial 来执行currying。

function includesAnyOf<T>(array: T[], ...searchElements: T[]) {
  return searchElements.some(_ => array.includes(_));
}

import { partial } from 'lodash';

const a = [1, 2, 3];
const b = 3;
const ext = (o) => {
  return {
    includesAnyOf: partial(includesAnyOf, o)
  };
};

console.log(
  ext(a).includesAnyOf(b) // true
);

【讨论】:

    【解决方案2】:
    function ext<T, E>(obj: T, extension: E) {
       return (receiver: (extended: T & E) => any) => {
          receiver(new Proxy(obj, {
            get(target, prop, receiver) {
              if(prop in extension)
                 return extension[prop];
              return Reflect.get(...arguments);
            },
            // TODO has, set, etc. according to needs
          } as T & E));
       };
    }
    
    ext(a, { includesAnyOf })(a => {
     a.includesAnyOf("stuff");
    });
    

    任务完成。

    function ext(obj, extension) {
           return (receiver) => {
              receiver(new Proxy(obj, {
                get(target, prop, receiver) {
                  if(prop in extension)
                     return extension[prop];
                  return Reflect.get(...arguments);
                },
                // TODO has, set, etc. according to needs
              }));
           };
        }
        
        
    function includesAnyOf(...searchElements) {
      return searchElements.some(_ => this.includes(_));
    }
        
    const a = [1, 2, 3];
    
    ext(a, { includesAnyOf })(a => {
      console.log(a.includesAnyOf("stuff"));
    });

    你能想出更好的方法吗?

    老实说,我认为常规函数没有任何好处。在 C# 和其他语言中,它很有用,因为您可以使用自动完成轻松找到这些扩展。无论你在 TS 中如何做到这一点,它都达不到这个目的。

    【讨论】:

    • 感谢您接受挑战!我的目标是有一个更易读的代码,但前提是它不会破坏强类型和自动完成。
    • 您的回答似乎不起作用。对于a.includesAnyOf("2"),它返回false。
    • @orad 字符串不是数字
    • 那是强类型的,你可以在内部 a 上自动完成,但你必须输入 ext(a, { ...methods })(a =&gt; ...) 才能获得扩展,而 IMO 那没用
    • 你是对的,我的错,但它不能为我编译,我只能在浏览器中运行它。是的,语法仍然很冗长,我同意它没那么有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    • 2015-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多