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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39877156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:23:21  来源:igfitidea点击:

How to extend String Prototype and use it next, in Typescript?

typescript

提问by bsides

I am extending String prototype chain with a new method but when I try to use it it throws me an error: property 'padZero' does not exist on type 'string'. Could anyone solve this for me?

我伸出字符串原型链用新的方法,但是当我尝试使用它,它会引发我一个错误:property 'padZero' does not exist on type 'string'。有人能帮我解决这个问题吗?

The code is below. You can also see the same error in Typescript Playground.

代码如下。您还可以在 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);
}

回答by GregRos

This answer applies to TypeScript 1.8+. There are lots of other answers to this sort of question, but they all seem to cover older versions.

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

There are two parts to extending a prototype in TypeScript.

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

Part 1 - Declare

第 1 部分 - 声明

Declaring the new member so it can pass type-checking. You need to declare an interface with the same name as the constructor/class you want to modify and put it under the correct declared namespace/module. This is called scope augmentation.

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

Extending the modules in this way can only be done in a special declaration .d.tsfiles*.

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

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

Types in external modules have names that include quotation marks, such as "bluebird".

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

The module name for global types such as Array<T>and Stringis global, without any quotes. However, in many versions of TypeScript you can forego the module declaration completely and declare an interface directly, to have something like:

全局类型的模块名称,例如Array<T>Stringis global,不带任何引号。但是,在许多版本的 TypeScript 中,您可以完全放弃模块声明并直接声明接口,例如:

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

This is the case in some versions pre-1.8, and also some versions post-2.0, such as the most recent version, 2.5.

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

Note that you cannot have anything except declarestatements in the .d.tsfile, otherwise it won't work.

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

These declarations are added to the ambient scope of your package, so they will apply in all TypeScript files even if you never importor ///<referencethe file directly. However, you still need to import the implementation you write in the 2nd part, and if you forget to do this, you'll run into runtime errors.

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

* Technically you can get past this restriction and put declarations in regular .tsfiles, but this results in some finicky behavior by the compiler, and it's not recommended.

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

Part 2 - Implement

第 2 部分 - 实施

Part 2 is actually implementing the member and adding it to the object it should exist on like you would do in JavaScript.

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

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

Note a few things:

请注意以下几点:

  1. String.prototypeinstead of just String, which is the Stringconstructor, rather than its prototype.
  2. I use an explicit functioninstead of an arrow function, because a functionwill correctly receive the thisparameter from where it's invoked. An arrow function will always use the same thisas the place it's declared in. The one time we don't want that to happen is when extending a prototype.
  3. The explicit this, so the compiler knows the type of thiswe expect. This part is only available in TS 2.0+. Remove the thisannotation if you're compiling for 1.8-. Without an annotation, thismay be implicitly typed any.
  1. String.prototype而不是 just String,它是String构造函数,而不是它的原型。
  2. 我使用显式function而不是箭头函数,因为 afunction将从this调用它的位置正确接收参数。箭头函数将始终使用与this它声明的位置相同的位置。我们不希望发生这种情况的一次是在扩展原型时。
  3. 显式this,所以编译器知道this我们期望的类型。此部分仅在 TS 2.0+ 中可用。this如果您正在为 1.8- 编译,请删除注释。没有注解,this可能是隐式类型的any

Import the JavaScript

导入 JavaScript

In TypeScript, the declareconstruct adds members to the ambient scope, which means they appear in all files. To make sure your implementation from part 2 is hooked up, importthe file right at the start of your application.

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

You import the file as follows:

您按如下方式导入文件:

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

Without importing anything. You can also import something from the file, but you don't need to import the function you defined on the prototype.

无需导入任何东西。你也可以从文件中导入一些东西,但是你不需要导入你在原型上定义的函数。

回答by jeremy.bass

Here is a working example, a simple Camel Case string modifier.

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

in my index.d.tsfor my project

在我的项目的index.d.ts

interface String {
    toCamelCase(): string;
}

in my root .tssomewhere accessible

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

That was all I needed to do to get it working in typescript ^2.0.10.

这就是我需要做的才能让它在 typescript 中工作^2.0.10

I use it like str.toCamelCase()

我用它喜欢 str.toCamelCase()

update

更新

I realized I had this need too and this is what I had

我意识到我也有这个需求,这就是我所拥有的

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);
};

so console.log('1214'.leadingChars('0', 10));gets me 0000001214

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

回答by Jacques

For me the following worked in an Angular 6 project using TypeScript 2.8.4.

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

In the typings.d.ts file add:

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

interface Number {
  padZero(length: number);
}

interface String {
  padZero(length: number);
}

Note: No need to 'declare global'.

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

In a new file called string.extensions.ts add the following:

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

To use it, first import it:

要使用它,首先导入它:

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

Obviously your import statement should point to the correct path.
Inside your class's constructor or any method:

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

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

回答by Steven Spungin

If you want to augment the class, and not the instance, augment the constructor:

如果你想增加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 {}

*The empty export on the bottom is required if you declare globalin .tsand do not export anything. This forces the file to be a module. *

*如果您declare global输入.ts并且不输出任何内容,则需要底部的空输出。这会强制文件成为模块。*

If you want the function on the instance(aka prototype),

如果你想要instance(又名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 {}