Usr
Usr

Reputation: 2838

Typescript String.format does not exist

I have a string const where I have to replace two words, like this:

public static readonly MY_STRING: string = 'page={0}&id={1}';

0 and 1 have to be replaced with other strings. I have read about String.format in different answers, where they suggest to supply an implementation like this:

if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) { 
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

but when I do String.format it tells me

Property 'format' does not exist on type 'String'

What is the proper way to use String interpolation/substitution in this case? With format I'd do something like this:

 MY_STRING.format(page, id)

How can I achieve this?

Upvotes: 2

Views: 6899

Answers (2)

jcalz
jcalz

Reputation: 328453

It is considered bad practice to modify native prototypes like String. Since there's no standard or agreed-upon format() method for strings in JavaScript, adding your own could lead to unexpected behavior in any code that runs in the same runtime. Your implementation even checks for an existing String.prototype.format first, which means if someone gets there first with a different implementation, then you could be the one with unexpected behavior.

There's absolutely nothing wrong with just having a stringFormat function sitting around that you use, like this:

function stringFormat(template: string, ...args: any[]) {
    return template.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != 'undefined'
            ? args[number]
            : match
            ;
    });
};

const myString: string = 'page={0}&id={1}';
const formattedWithFormat = stringFormat(myString, 123, 456);
console.log(formattedWithFormat); // page=123&id=456

Also, JavaScript has template literals which provide essentially the same functionality:

const myTemplate = (page: number, id: number) => `page=${page}&id=${id}`;
const formattedWithTemplate = myTemplate(123, 456);
console.log(formattedWithTemplate); // page=123&id=456

If you are intent on modifying the prototype of String and the previous warnings didn't dissuade you, then you could use the global augmentation or module augmentation approach to allow TypeScript to recognize that you expect string values to have a format() method:

/* 🐉 here be dragons 🐲 */
interface String {
    format(...args: any[]): string;
}
String.prototype.format = function (...args) { return stringFormat(String(this), ...args) };
console.log(myString.format(123, 789)); // page=123&id=789

but hopefully you will use one of the other solutions.


Okay, hope that helps; good luck!

Playground link

Upvotes: 7

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249706

You can augment the declaration of string from the base library:

declare global {
    interface String {
        format(...args: []): string
    }
}

Playground Link

Note: If not in a module the declare global is not necessary, you can just move the interface String {...}to the top level (Playground Link)

Upvotes: 0

Related Questions