【问题标题】:How to properly extend Array prototype in Typescript 4.0.3?如何在 Typescript 4.0.3 中正确扩展 Array 原型?
【发布时间】:2021-02-07 20:16:09
【问题描述】:

我正在尝试使用一些自定义方法扩展基本Array 接口。我查看了 SO 和 typescript 文档,最后将以下代码放在一起:

// In module Func.ts

declare global {
  type Array<T> = {
    intersperse(mkT: (ix: number) => T): T[];
  };
}

if (!('intersperse' in Array.prototype)) {
  Array.prototype.intersperse = function intersperse<T>(this: T[], mkT: (ix: number) => T): T[] {
    return this.reduce((acc: T[], d, ix) => [...acc, mkT(ix), d], []).slice(1);
  };
}

但是,我收到以下错误:

// On type Array<T> = { ... }
Duplicate identifier 'Array'.ts(2300)

// On Array.prototype.intersperse = ...
Property 'intersperse' does not exist on type 'any[]'.ts(2339)

另外,每当我尝试在其他文件中使用 intersperse 时,我都会收到错误消息

Property 'intersperse' does not exist on type 'Element[]'.ts(2339)

这是意料之中的,考虑到Func.ts 中的声明似乎不起作用。据此,我认为 SO 问题已经过时(或不完整),并且从那以后发生了一些变化。

那么,为了摆脱这些错误并扩展 Array 原型,最好的扩展方法是什么?在你说我不应该这样做之前——是的,我知道all the risks,而且我还是做出了明智的决定。

【问题讨论】:

  • 如果您要扩展原生原型,请确保您的扩展是不可枚举的,方法是使用带有适当标志的 Object.defineProperty
  • @T.J.Crowder 好主意,我没想到!谢谢。
  • @str 我知道即使我包含“我做出了明智的决定还是要这样做”部分,也会有人抱怨我在做什么。我知道你的意思是好的,但它仍然很有趣,
  • @str 公平点。谢谢你。我将编辑我的问题以包含您当时发布的链接。

标签: arrays typescript interface prototype


【解决方案1】:

Array 被定义为接口而不是类型。 typescript 中的接口是开放式的,可以通过多个声明添加。类型不共享相同的功能。

export{}
declare global {
  interface Array<T>  {
    intersperse(mkT: (ix: number) => T): T[];
  }
}

if (!Array.prototype.intersperse) {
  Array.prototype.intersperse = function intersperse<T>(this: T[], mkT: (ix: number) => T): T[] {
    return this.reduce((acc: T[], d, ix) => [...acc, mkT(ix), d], []).slice(1);
  };
}

Playground Link

作为 T.J. Crowder 提到您可以考虑使用Object.defineProperty 来确保该属性不可枚举:

export {}
declare global {
  interface Array<T>  {
    intersperse(mkT: (ix: number) => T): T[];
  }
}

if (!Array.prototype.intersperse) {
  Object.defineProperty(Array.prototype, 'intersperse', {
    enumerable: false, 
    writable: false, 
    configurable: false, 
    value: function intersperse<T>(this: T[], mkT: (ix: number) => T): T[] {
      return this.reduce((acc: T[], d, ix) => [...acc, mkT(ix), d], []).slice(1);
    }
  });
}

Playground Link

【讨论】:

  • 谢谢,我不知道这种类型/接口的区别!最重要的是,我被typescript-eslint/consistent-type-definitions 咬了,它立即将interface 更改为type,而我没有注意到它。当然,SO 上的示例代码使用了interface
  • 在这种情况下,!Array.prototype.intersperse 是检查属性存在的推荐方法吗?通常我会使用in 来消除 TS 警告“属性可能不存在”,但实际上建议以这种方式进行检查,而不是无意中覆盖不可枚举的属性。我的想法对吗?
  • 只是 FWIW,内置原型上的方法通常是可写和可配置的,只是不可枚举。
  • @T.J.Crowder in 的问题是打字稿问题。 in 充当Array.prototype 的类型保护,并且由于intersperse 被声明为始终存在于Array.prototype,类型保护!('intersperse' in Array.prototype) 将使Array.prototype 类型为never(因为TS 相信这种情况就声明的类型而言,永远不会是真的)。 undefined 测试不作为 Array.prototype 的类型保护,仅作为 Array.prototype.intersperse 的类型保护,因为我们只是分配它,所以编译器认为 Array.prototype.intersperse 是什么类型并不重要
猜你喜欢
  • 2022-01-21
  • 2017-03-31
  • 2021-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
相关资源
最近更新 更多