未捕获的TypeError:Typescript扩展方法不是函数

时间:2019-06-16 22:54:43

标签: typescript

我正在尝试为Array编写扩展方法。每当程序运行代码时,我得到Uncaught TypeError: pairs.map(...).flatten is not a function。代码如下所示:

extensions.ts

declare interface Array<T> {
  flatten<T>(): T;
}

Array.prototype.flatten = function<T> () : T {
  return this.reduce(function (flat: Array<T>, toFlatten: Array<T>) {
    return flat.concat(Array.isArray(toFlatten) ? toFlatten.flatten() : toFlatten )
  }, []);
}

values.ts

export let pairs: { start: String[], end: String[] }[] = [
  { start: [ ... ], end: [ ... ] }, ...
];
export let items: String[] = pairs.map(pair => pair.start.concat(pair.end)).flatten();

我尝试将Array.prototype.flatten = function<T> () : T { ...更改为Array.prototype.flatten = <T> () : T => { ...,将pairs.map(pair => pair.start.concat(pair.end)).flatten()更改为pairs.map(pair => pair.start.concat(pair.end)).flatten<String[]>(),但是它什么也没做。

我还在这里某处读到它可能是一个转码问题,因此我将--target编译器更改为ESNext,但错误仍然弹出。

2 个答案:

答案 0 :(得分:0)

欢迎来到SO,b12629?

您看到的是运行时异常。尽管类型定义为TS提供了编译代码所需的一切,但是您需要确保在条目文件顶部附近(在调用之前)包含sheet4的定义,否则JS引擎将获得胜利不知道它存在于flatten原型中。

答案 1 :(得分:0)

不幸的是,TypeScript在编译时加载模块的方式与Javascript在运行时加载模块的方式大不相同(尤其是在使用具有自己的Javascript模块加载系统的诸如React或Jest之类的框架时)。模块加载系统的这种差异意味着,即使您的代码可以编译,它也可能在运行时失败(并且是否失败很大程度上取决于您所使用的特定框架以及模块的导入/导出,从而导致代码非常脆弱)。因此,很难可靠地在TypeScript中使用扩展方法,因此建议使用替代方法(例如helper方法,自定义类等)。

但是,如果您确实想使Extension方法起作用,请继续阅读。

从根本上讲,该错误意味着TypeScript在编译期间已正确定位了接口扩展名:

declare interface Array<T> {
  flatten<T>(): T;
}

但是Javascript尚未在运行时执行扩展原型的调用:

Array.prototype.flatten = function<T> () : T {
  return this.reduce(function (flat: Array<T>, toFlatten: Array<T>) {
    return flat.concat(Array.isArray(toFlatten) ? toFlatten.flatten() : toFlatten )
  }, []);
}

不幸的是,当前在StackOverflow上接受的答案和其他相关答案仅提及将定义放入“顶级文件”中,而该文件并未提供足够清晰的说明来处理所有情况(请参阅How to create an extension method in TypeScript for 'Date' data typeTypescript extension method compiling but not working at run timeTypescript Extend String interface Runtime Error)。

要调试不一致的行为,可以在原型扩展调用上放置一个断点,以查看何时/是否调用了它。您还可以检查扩展类的proto字段,以检查扩展方法是否已在运行时创建。请记住,由于Tree Shaking,即使添加多余的导入行也不足以迫使JavaScript评估该行。

要确保发生原型扩展调用,请找到所有应用程序入口点(例如Jest测试,全局Web框架等),并确保从每个入口点调用原型扩展调用(粗略的解决方案是注意:请记住,Jest和其他测试库可以模拟模块,因此,请确保不要模拟调用原型扩展的模块。意味着您应该为扩展名使用单独的文件,并禁用Jest自动模拟功能(或等效功能)。