如何在Typescript中扩展String Prototype并使用它?

bsi*_*des 41 typescript

我正在使用一种新方法扩展String原型链,但是当我尝试使用它时,它会抛出一个错误:property 'padZero' does not exist on type 'string'.谁能为我解决这个问题?

代码如下.您还可以在Typescript Playground中看到相同的错误.

interface NumberConstructor {
    padZero(length: number);
}
interface StringConstructor {
    padZero(length: number): string;
}
String.padZero = (length: number) => {
    var s = this;
    while (s.length < length) {
      s = '0' + s;
    }
    return s;
};
Number.padZero = function (length) {
    return String(this).padZero(length);
}
Run Code Online (Sandbox Code Playgroud)

Gre*_*Ros 79

此答案适用于TypeScript 1.8+.这类问题有很多其他答案,但它们似乎都涵盖旧版本.

在TypeScript中扩展原型有两个部分.

第1部分 - 声明

声明新成员,以便它可以传递类型检查.您需要声明一个与要修改的构造函数/类同名的接口,并将其放在正确的声明的命名空间/模块下.这称为范围扩充.

以这种方式扩展模块只能在特殊的声明.d.ts文件*中完成.

//in a .d.ts file:
declare global {
    interface String {
        padZero(length : number) : string;
    }
}
Run Code Online (Sandbox Code Playgroud)

外部模块中的类型具有包含引号的名称,例如"bluebird".

全球类型,如模块名称Array<T>Stringglobal,没有任何报价.但是,在许多版本的TypeScript中,您可以完全放弃模块声明并直接声明接口,具有以下内容:

declare interface String {
        padZero(length : number) : string;
}
Run Code Online (Sandbox Code Playgroud)

在1.8之前的某些版本中也是这种情况,在2.0之后的某些版本中也是如此,例如最新版本2.5.

请注意,除了文件中的declare语句之外,您不能拥有任何内容.d.ts,否则它将无效.

这些声明被添加到包的环境范围中,因此它们将应用于所有TypeScript文件,即使您从不直接import///<reference直接使用文件.但是,您仍然需要导入在第二部分中编写的实现,如果忘记执行此操作,则会遇到运行时错误.

*从技术上讲,你可以通过这个限制并将声明放在常规.ts文件中,但这会导致编译器出现一些挑剔的行为,并且不建议这样做.

第2部分 - 实施

第2部分实际上是实现成员并将其添加到它应该存在的对象中,就像在JavaScript中一样.

String.prototype.padZero = function (this : string, length: number) {
    var s = this;
    while (s.length < length) {
      s = '0' + s;
    }
    return s;
};
Run Code Online (Sandbox Code Playgroud)

请注意以下几点:

  1. String.prototype而不仅仅是String,它是String构造函数,而不是它的原型.
  2. 我使用显式function而不是箭头函数,因为a function将正确地this从调用它的位置接收参数.箭头函数将始终使用this与其声明的位置相同的内容.有一次我们不希望发生这种情况是在扩展原型时.
  3. 显式this,所以编译器知道this我们期望的类型.此部分仅适用于TS 2.0+.this如果您正在编译1.8-,请删除注释.没有注释,this可以隐式输入any.

导入JavaScript

在TypeScript中,declare构造将成员添加到环境范围,这意味着它们出现在所有文件中.为了确保第2部分的实现已连接,import文件就在应用程序的开头.

您导入文件如下:

import '/path/to/implementation/file';
Run Code Online (Sandbox Code Playgroud)

没有任何进口.您也可以从文件中导入某些内容,但不需要导入在原型上定义的函数.

  • 此外,如果您尝试将其放在2.5.1中的`.d.ts`文件中,则会出现以下错误:`全局范围的扩充只能直接嵌套在外部模块或环境模块声明中. (5认同)

jer*_*ass 20

这是一个工作示例,一个简单的Camel Case字符串修饰符.

在我的index.d.ts中为我的项目

interface String {
    toCamelCase(): string;
}
Run Code Online (Sandbox Code Playgroud)

在我的根本.ts可访问的地方

String.prototype.toCamelCase = function(): string { 
    return this.replace(/(?:^\w|[A-Z]|-|\b\w)/g, 
       (ltr, idx) => idx === 0
              ? ltr.toLowerCase()
              : ltr.toUpperCase()
    ).replace(/\s+|-/g, '');
};
Run Code Online (Sandbox Code Playgroud)

这就是我需要做的就是让它在打字稿中工作^2.0.10.

我喜欢它 str.toCamelCase()

更新

我意识到我也有这种需要,这就是我所拥有的

interface String {
    leadingChars(chars: string|number, length: number): string;
}


String.prototype.leadingChars = function (chars: string|number, length: number): string  {
    return (chars.toString().repeat(length) + this).substr(-length);
};
Run Code Online (Sandbox Code Playgroud)

所以console.log('1214'.leadingChars('0', 10));得到我0000001214


Jac*_*ues 12

对我来说,以下使用TypeScript 2.8.4在Angular 6项目中工作.

在typings.d.ts文件中添加:

interface Number {
  padZero(length: number);
}

interface String {
  padZero(length: number);
}
Run Code Online (Sandbox Code Playgroud)

注意:无需'声明全局'.

在名为string.extensions.ts的新文件中添加以下内容:

interface Number {
  padZero(length: number);
}

interface String {
  padZero(length: number);
}

String.prototype.padZero = function (length: number) {
  var s: string = String(this);
  while (s.length < length) {
    s = '0' + s;
  }
  return s;
}

Number.prototype.padZero = function (length: number) {
  return String(this).padZero(length)
}
Run Code Online (Sandbox Code Playgroud)

要使用它,首先导入它:

import '../../string.extensions';
Run Code Online (Sandbox Code Playgroud)

显然你的import语句应该指向正确的路径.
在你的类的构造函数或任何方法中:

var num: number = 7;
var str: string = "7";
console.log("padded number: ", num.padZero(5));
console.log("padding string: ", str.padZero(5));
Run Code Online (Sandbox Code Playgroud)

  • 您能解释一下如何使用typing.d.ts 文件吗? (2认同)

Ste*_*gin 10

如果要扩充class而不是instance,请扩充构造函数:

declare global {
  interface StringConstructor {
    padZero(s: string, length: number): string;
  }
}

String.padZero = (s: string, length: number) => {
  while (s.length < length) {
    s = '0' + s;
  }
  return s;
};

console.log(String.padZero('hi', 5))

export {}
Run Code Online (Sandbox Code Playgroud)

*需要,如果你在底部空的出口declare global.ts,并没有出口任何东西。这将强制文件成为模块。*

如果要在instance(aka prototype)上使用该功能,

declare global {
  interface String {
    padZero(length: number): string;
  }
}

String.prototype.padZero = function (length: number) {
  let d = String(this)
  while (d.length < length) {
    d = '0' + d;
  }
  return d;
};

console.log('hi'.padZero(5))

export {}
Run Code Online (Sandbox Code Playgroud)

  • 令人遗憾的是,如此设计糟糕的语言却获得了如此广泛的流行...... (5认同)
  • 令人遗憾的是您没有花时间去理解语言中的设计选择。 (2认同)
  • 在我的实验中,放置“declare global”会产生编译错误——“全局作用域的增强只能直接嵌套在外部模块或环境模块声明中”。这是否仍然可以增强 String,而不必每次都导入某些内容? (2认同)

tod*_*dmo 8

2022 打字稿 4.9

解决方案非常简单,但实现该解决方案却并非如此。忽略得票最高的答案;其中大部分是不需要的,也不再起作用了。

索引.d.ts

不需要!

字符串.ts

interface String {
    toProperCase(): string;
    bool(): boolean;
}

String.prototype.toProperCase = function (): string {
    return this.toLowerCase().replace(/\b\w/g, (c: string) => c.toUpperCase())
}

String.prototype.bool = function (): boolean {
    return this.toLowerCase() == 'true';
};
Run Code Online (Sandbox Code Playgroud)

使用它

import './lib/strings'

var a = 'hello'
var b = a.toProperCase()
Run Code Online (Sandbox Code Playgroud)

输出:

你好

  • 如果我们在“strings.ts”中的“interface String”之上有一些导入(比如说用于日志记录的辅助函数)。TypeScript 给出错误“‘String’类型上不存在属性‘x’”。@toddmo 你知道如何解决这个问题吗? (2认同)