【问题标题】:How to extend String Prototype and use it next, in Typescript?如何在 Typescript 中扩展字符串原型并在接下来使用它?
【发布时间】:2017-02-14 02:05:03
【问题描述】:

我正在用一种新方法扩展 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);
}

【问题讨论】:

    标签: typescript


    【解决方案1】:

    如果你想增加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)上的功能,

    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 {}
    

    【讨论】:

    • 我已经完成了第二个选项,但由于某些原因在运行时我仍然得到 isNullOrEmpty is not a function。
    • 那不是打字稿问题。你应该调试看看它是什么运行时类型。
    • 设法找出我的错误:我扩充了实例,当您尝试测试 Null OR Empty 时,考虑到变量是否为 null 您最终会出现异常,这有点毫无意义我有。我将其更改为增加解决了我的问题的课程。感谢您在回答中的洞察力。
    • 很遗憾,这么糟糕的设计语言却获得了如此多的欢迎......
    • 很遗憾您没有花时间了解语言中的设计选择。
    【解决方案2】:

    对我来说,以下是在使用 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';
    

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

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

    【讨论】:

    • 唯一适用于 Node.JS,TypeScript 3.3.1 的答案
    • 您能解释一下如何使用 typing.d.ts 文件吗?
    • 如果您的文件使用imports,您的界面将隐式变为模块范围,因此需要declare global
    【解决方案3】:

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

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

    第 1 部分 - 声明

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

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

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

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

    Array&lt;T&gt;String 等全局类型的模块名称为global,不带任何引号。但是,在许多 TypeScript 版本中,您可以完全放弃模块声明并直接声明接口,以具有如下内容:

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

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

    请注意,.d.ts 文件中不能有除declare 语句之外的任何内容,否则将不起作用。

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

    * 从技术上讲,您可以克服此限制并将声明放入常规的 .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

    导入 JavaScript

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

    您按如下方式导入文件:

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

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

    【讨论】:

    • 我使用的是 v2.1.0-dev.20161001。我以前试过这个,但它也给了我上面declare global 的错误。另外,我注意到 stackoverflow 的很多答案告诉我们在没有原型的情况下进行扩展,但生成的代码没有使用它......这里有什么问题?为什么这么难?
    • 另外,如果您尝试将其放入 2.5.1 中的 .d.ts 文件中,则会收到以下错误:Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
    • @GregRos 是的,我的.d.ts 文件中还有其他内容。我把它放在根types.d.ts 中,它似乎在没有声明的情况下工作。感谢您澄清导入。
    • 嘿,这是一个很好的答案,我想知道您是否知道在非环境上下文中修改全局接口的任何方法?
    • @PatrickRoberts 如果类型声明在包内,则只有imports 包的应用程序才能看到它们。除此之外,真的没有办法直接做到这一点。但也许还有其他选择。打开一个问题并解释你的意思。
    【解决方案4】:

    这是一个工作示例,一个简单的 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, '');
    };
    

    这就是让它在 typescript ^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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 2016-12-26
      • 1970-01-01
      • 1970-01-01
      • 2021-02-07
      • 2016-10-23
      相关资源
      最近更新 更多