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

时间:2016-10-05 14:46:13

标签: 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);
}

4 个答案:

答案 0 :(得分:63)

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

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

第1部分 - 声明

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

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

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

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

Array<T>String等全局类型的模块名称为global,没有任何引号。但是,在许多版本的TypeScript中,您可以完全放弃模块声明并直接声明接口,具有类似的内容:

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

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

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

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

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

第2部分 - 实施

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

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

请注意以下几点:

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

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

    您可以按如下方式导入文件:

    import '/path/to/implementation/file';
    

    不导入任何东西。您也可以从文件中导入某些内容,但不需要导入您在原型上定义的函数。

答案 1 :(得分:17)

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

在我的项目 index.d.ts

interface String {
    toCamelCase(): string;
}

在我的根.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, '');
};

这就是我需要做的就是让它在打字稿^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);
};

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

答案 2 :(得分:7)

对我来说,以下内容使用TypeScript 2.8.4在Angular 6项目中进行了工作。

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

interface Number {
  padZero(length: number);
}

interface String {
  padZero(length: number);
}

注意:无需“声明全局”。

在名为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)
}

要使用它,请先将其导入:

import '../../string.extensions';

很显然,您的import语句应指向正确的路径。
在类的构造函数或任何方法中:

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

答案 3 :(得分:3)

如果要扩展$Records = Import-Csv -Path C:\server2.log -Delimiter "," -Header Date, Message $FixedRecords = for($i = 0; $i -lt $Records.Count - 1; $i++){ $NextRecord = $Records[$i + 1] if($NextRecord.Date -notmatch '^\d{4}-\d{2}-\d{2}'){ # Append next record's message to the current one $Records[$i].Message += $NextRecord.Message # Skip ahead to the next record $i++ } $Records[$i] } 而不是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 {} declare global并且不导出任何内容,则必须在底部进行空导出。这将强制文件成为模块。 *

如果要在.ts(又名instance)上使用该功能,

prototype