Hoe kan ik String Prototype uitbreiden en vervolgens gebruiken in Typescript?

Ik breid de String-prototypeketen uit met een nieuwe methode, maar wanneer ik deze probeer te gebruiken, krijg ik een foutmelding: property 'padZero' does not exist on type 'string'. Kan iemand dit voor mij oplossen?

De code staat hieronder. Je kunt dezelfde fout ook zien in 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);
}

Antwoord 1, autoriteit 100%

Dit antwoord is van toepassing op TypeScript 1.8+. Er zijn veel andere antwoorden op dit soort vragen, maar ze lijken allemaal betrekking te hebben op oudere versies.

Het uitbreiden van een prototype in TypeScript bestaat uit twee delen.

Deel 1 – Verklaren

Het nieuwe lid declareren zodat het de typecontrole kan doorstaan. U moet een interface declareren met dezelfde naam als de constructor/klasse die u wilt wijzigen en deze onder de juiste gedeclareerde naamruimte/module plaatsen. Dit wordt scope-vergrotinggenoemd.

Het op deze manier uitbreiden van de modules kan alleen in een speciale aangifte .d.tsbestanden*.

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

Typen in externe modules hebben namen die aanhalingstekens bevatten, zoals "bluebird".

De modulenaam voor globale typen zoals Array<T>en Stringis global, zonder aanhalingstekens. In veel versies van TypeScript kunt u de moduledeclaratie echter volledig achterwege laten en direct een interface declareren, om zoiets te krijgen als:

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

Dit is het geval in sommige versies van vóór 1.8 en ook in sommige versies van na 2.0, zoals de meest recente versie 2.5.

Houd er rekening mee dat u niets anders kunt hebben dan declare-instructies in het .d.ts-bestand, anders werkt het niet.

Deze declaraties worden toegevoegd aan de omgevingsscope van uw pakket, zodat ze van toepassing zijn op alle TypeScript-bestanden, zelfs als u nooit de importof ///<referencede direct bestand. U moet echter nog steeds de implementatie importeren die u in het 2e deel schrijft, en als u dit vergeet, loopt u tegen runtime-fouten aan.

* Technisch gezien kun je deze beperking omzeilen en declaraties in gewone .ts-bestanden plaatsen, maar dit resulteert in wat kieskeurig gedrag van de compiler, en het wordt niet aanbevolen.

Deel 2 – Implementeren

Deel 2 implementeert in feite het lid en voegt het toe aan het object waarop het zou moeten bestaan, zoals u zou doen in JavaScript.

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

Let op een paar dingen:

  1. String.prototypein plaats van alleen String, wat de String-constructor is, in plaats van zijn prototype.
  2. Ik gebruik een expliciete functionin plaats van een pijlfunctie, omdat een functioncorrect de parameter thisontvangt van waaruit het wordt aangeroepen. Een pijlfunctie gebruikt altijd dezelfde thisals de plaats waarin deze is gedeclareerd. De enige keer dat we niet willen dat dit gebeurt, is bij het uitbreiden van een prototype.
  3. De expliciete this, zodat de compiler weet welk type thiswe verwachten. Dit onderdeel is alleen beschikbaar in TS 2.0+. Verwijder de annotatie thisals u compileert voor 1.8-. Zonder annotatie kan thisimpliciet als anyworden getypt.

Importeer het JavaScript

In TypeScript voegt de constructie declareleden toe aan de ambient scope, wat betekent dat ze in alle bestanden verschijnen. Om er zeker van te zijn dat je implementatie uit deel 2 is aangesloten, importhet bestand direct aan het begin van je applicatie.

U importeert het bestand als volgt:

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

Zonder iets te importeren. U kunt ook iets uit het bestand importeren, maar u hoeft de functie die u op het prototype hebt gedefinieerd niet te importeren.


Antwoord 2, autoriteit 42%

Als u de classwilt uitbreiden, en niet de instance, vergroot u de constructor:

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

*De lege export onderaan is vereist als je declare globaldeclareert in .tsen niets exporteert. Dit dwingt het bestand om een ​​module te zijn. *

Als u de functie op de instance(ook bekend als prototype) wilt,

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

Antwoord 3, autoriteit 23%

Hier is een werkend voorbeeld, een eenvoudige Camel Case string-modifier.

in mijn index.d.tsvoor mijn project

interface String {
    toCamelCase(): string;
}

in mijn root .tsergens toegankelijk

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

Dat was alles wat ik moest doen om het werkend te krijgen in typoscript ^2.0.10.

Ik gebruik het zoals str.toCamelCase()

bijwerken

Ik realiseerde me dat ik deze behoefte ook had en dit is wat ik 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);
};

dus console.log('1214'.leadingChars('0', 10));krijg ik 0000001214


Antwoord 4, autoriteit 22%

Voor mij werkte het volgende in een Angular 6-project met TypeScript 2.8.4.

Voeg toe aan het bestand typings.d.ts:

interface Number {
  padZero(length: number);
}
interface String {
  padZero(length: number);
}

Opmerking: het is niet nodig om ‘globaal te declareren’.

Voeg in een nieuw bestand met de naam string.extensions.ts het volgende toe:

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

Om het te gebruiken, importeer het eerst:

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

Het is duidelijk dat uw importstatement naar het juiste pad moet verwijzen.
Binnen de constructor van je klasse of een andere methode:

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

Other episodes