String.Format not work in TypeScript
Asked Answered
C

10

86

String.Format does not work in TypeScript.
Error:

The property 'format' does not exist on value of type 
 '{ prototype: String; fromCharCode(...codes: number[]): string; 
 (value?: any): string; new(value?: any): String; }'.

attributes["Title"] = String.format(
    Settings.labelKeyValuePhraseCollection["[WAIT DAYS]"],
    originalAttributes.Days
);
Catto answered 19/11, 2013 at 11:21 Comment(2)
Why do you think that String.format exists out of the box?Machinegun
So how can I add String.format?Befitting
C
8

You can declare it yourself quite easily:

interface StringConstructor {
    format: (formatString: string, ...replacement: any[]) => string;
}

String.format('','');

This is assuming that String.format is defined elsewhere. e.g. in Microsoft Ajax Toolkit : http://www.asp.net/ajaxlibrary/Reference.String-format-Function.ashx

Cuisse answered 19/11, 2013 at 11:30 Comment(2)
There is already an interface for String in lib.d.ts so you can just extend that as interfaces are open.Burgoo
Cannot redeclare block-scoped variable 'String'.Raffle
B
172

String Interpolation

Note: As of TypeScript 1.4, string interpolation is available in TypeScript:

var a = "Hello";
var b = "World";

var text = `${a} ${b}`

This will compile to:

var a = "Hello";
var b = "World";
var text = a + " " + b;

String Format

The JavaScript String object doesn't have a format function. TypeScript doesn't add to the native objects, so it also doesn't have a String.format function.

For TypeScript, you need to extend the String interface and then you need to supply an implementation:

interface String {
    format(...replacements: string[]): string;
}

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

You can then use the feature:

var myStr = 'This is an {0} for {0} purposes: {1}';

alert(myStr.format('example', 'end'));

You could also consider string interpolation (a feature of Template Strings), which is an ECMAScript 6 feature - although to use it for the String.format use case, you would still need to wrap it in a function in order to supply a raw string containing the format and then positional arguments. It is more typically used inline with the variables that are being interpolated, so you'd need to map using arguments to make it work for this use case.

For example, format strings are normally defined to be used later... which doesn't work:

// Works
var myFormatString = 'This is an {0} for {0} purposes: {1}';

// Compiler warnings (a and b not yet defines)
var myTemplateString = `This is an ${a} for ${a} purposes: ${b}`;

So to use string interpolation, rather than a format string, you would need to use:

function myTemplate(a: string, b: string) {
    var myTemplateString = `This is an ${a} for ${a} purposes: ${b}`;
}

alert(myTemplate('example', 'end'));

The other common use case for format strings is that they are used as a resource that is shared. I haven't yet discovered a way to load a template string from a data source without using eval.

Burgoo answered 19/11, 2013 at 11:41 Comment(7)
"JavaScript (and therefore TypeScript) doesn't have a native String.Format function" - this is not actually correct. There are many things that exist in TypeScript that don't exist in JavaScript (the reverse of what you said is true however)Distinct
@YannDuran can you give me an example on one native object that has been extended in TypeScript? The statement is pointing out that at runtime you will call exactly the same String object as someone using JavaScript.Burgoo
I didn't say anything about any native object being extended in TypeScript, I said that your statement as you wrote it is incorrect. Type Script is a superset of Javascript, therefore there are many constructs that exist in TypeScript that don't exist in Javascript. "With the 1.4 release, TypeScript now supports ES6 template strings and can also compile them down to ES3/ES5 expressions". You would use this instead if string.format to achieve the same result.Distinct
@YannDuran I have reworded the first sentence in order to attempt to allow it to still work when people only want to read parts of it in isolation. Template strings do exist in JavaScript. Where the compiler adds a polyfill for older targets, it still doesn't add to the native objects - it inlines the change, or (more rarely) adds a specially named function. Template strings do not satisfy all use-cases of String.format.Burgoo
You obviously think I was having a go at you - I wasn't. But you seem to have missed my point. As I stated in my last reply, I never said, or implied, that anything was being added to any native object. I was simply pointing out that your statement that just because JS didn't have some feature that TS therefore also didn't have it was not correct. I didn't want anyone reading this post to take your statement to be true. Your statement was not incorrect because I was reading it in isolation, it simply wasn't correct. I hope you now understand what I was getting at.Distinct
@YannDuran The keyword was "native". If you drop the crux of what he was saying and then say you're correct, maybe you are, but it has nothing to do with what he was talking about. I like flying kites. I like flying...see the difference?Supersensitive
Strings with { } don't seem to work from localised files...Debtor
Y
73

You can use TypeScript's native string interpolation in case if your only goal to eliminate ugly string concatenations and boring string conversions:

var yourMessage = `Your text ${yourVariable} your text continued ${yourExpression} and so on.`

NOTE:

At the right side of the assignment statement the delimiters are neither single or double quotes, instead a special char called backtick or grave accent.

The TypeScript compiler will translate your right side special literal to a string concatenation expression. With other words this syntax does not rely on the ECMAScript 6 feature, instead a native TypeScript feature. Your generated javascript code remains compatible.

Yehudit answered 17/6, 2015 at 8:25 Comment(1)
I couldn't get string interpolation to work for the longest time until I saw your note about the special tick mark and realized what I was doing wrong.Czech
M
17

I solved it like this;

1.Created a function

export function FormatString(str: string, ...val: string[]) {
  for (let index = 0; index < val.length; index++) {
    str = str.replace(`{${index}}`, val[index]);
  }
  return str;
}

2.Used it like the following;

FormatString("{0} is {1} {2}", "This", "formatting", "hack");
Masticate answered 24/1, 2019 at 10:58 Comment(3)
Ok but won't work if you want to reuse the same value in multiple places in the string. You can't do for example : FormatString("{0} is happy because {0} solved his problem", "jeremy"); The second placeholder will be omitted.Nonobservance
Really? I looked this up, and it seems .replace() is a "replace all" function that uses regex.Debtor
FormatString("{0} is {1} {2}", "This {1}", "formatting", "hack"); will return "This formatting formatting hack", but not "This {1} formatting hack"Hippocampus
P
9

If you are using NodeJS, you can use the build-in util function:

import * as util from "util";
util.format('My string: %s', 'foo');

Document can be found here: https://nodejs.org/api/util.html#util_util_format_format_args

Participial answered 28/12, 2020 at 8:43 Comment(1)
Please, note that the documentation says: "… is intended as a debugging tool. Some input values can have a significant performance overhead that can block the event loop.". This is written at the bottom of the function's documentation and thus it is very easy to miss.Cuccuckold
C
8

You can declare it yourself quite easily:

interface StringConstructor {
    format: (formatString: string, ...replacement: any[]) => string;
}

String.format('','');

This is assuming that String.format is defined elsewhere. e.g. in Microsoft Ajax Toolkit : http://www.asp.net/ajaxlibrary/Reference.String-format-Function.ashx

Cuisse answered 19/11, 2013 at 11:30 Comment(2)
There is already an interface for String in lib.d.ts so you can just extend that as interfaces are open.Burgoo
Cannot redeclare block-scoped variable 'String'.Raffle
L
4

FIDDLE: https://jsfiddle.net/1ytxfcwx/

NPM: https://www.npmjs.com/package/typescript-string-operations

GITHUB: https://github.com/sevensc/typescript-string-operations

I implemented a class for String. Its not perfect but it works for me.

use it i.e. like this:

var getFullName = function(salutation, lastname, firstname) {
    return String.Format('{0} {1:U} {2:L}', salutation, lastname, firstname)
}

export class String {
    public static Empty: string = "";

    public static isNullOrWhiteSpace(value: string): boolean {
        try {
            if (value == null || value == 'undefined')
                return false;

            return value.replace(/\s/g, '').length < 1;
        }
        catch (e) {
            return false;
        }
    }

    public static Format(value, ...args): string {
        try {
            return value.replace(/{(\d+(:.*)?)}/g, function (match, i) {
                var s = match.split(':');
                if (s.length > 1) {
                    i = i[0];
                    match = s[1].replace('}', '');
                }

                var arg = String.formatPattern(match, args[i]);
                return typeof arg != 'undefined' && arg != null ? arg : String.Empty;
            });
        }
        catch (e) {
            return String.Empty;
        }
    }

    private static formatPattern(match, arg): string {
        switch (match) {
            case 'L':
                arg = arg.toLowerCase();
                break;
            case 'U':
                arg = arg.toUpperCase();
                break;
            default:
                break;
        }

        return arg;
    }
}

EDIT:

I extended the class and created a repository on github. It would be great if you can help to improve it!

https://github.com/sevensc/typescript-string-operations

or download the npm package

https://www.npmjs.com/package/typescript-string-operations

Lali answered 2/2, 2016 at 21:0 Comment(3)
Any plunker or fiddle ?Yves
good work, already using your package with few modifications.Yves
great work, this is one stop shop for everything i need. However, this is only compatible with webpack 2.2.0 and I am using 3.3.0.Callie
B
1

If you don't want to import more libraries or clutter your code you can do this: const template = (value) => `my template ${value}`; Then when you want to format the template simply call template like this: template('hello world').

Hopes this helps, anyone else who comes across this in the future. (btw this is my first stack answer)

Brion answered 6/6, 2023 at 20:22 Comment(0)
N
0

As a workaround which achieves the same purpose, you may use the sprintf-js library and types.

I got it from another SO answer.

Noonan answered 20/1, 2017 at 10:2 Comment(0)
B
0

I create a method by mixing answers of @AnandShanbhag @Fenton

formatMessage(message: string, ...params: any[]):string {
let regexp = /{(\d+)}/g;
return message.replace(regexp, function (match, number): string {
    return (number < params.length && typeof params[number] != "undefined") ? params[number] : match;
});
}
Byroad answered 7/11, 2022 at 5:24 Comment(0)
D
-6

I am using TypeScript version 3.6 and I can do like this:

let templateStr = 'This is an {0} for {1} purpose';

const finalStr = templateStr.format('example', 'format'); // This is an example for format purpose
Demarcate answered 16/10, 2019 at 20:51 Comment(1)
How? I get Property 'format' does not exist on type 'string'. in TS > 4.Garnishment

© 2022 - 2024 — McMap. All rights reserved.