Перетворити рядок у рядок шаблону


147

Чи можливо створити рядок шаблону як звичайний рядок

let a="b:${b}";

потім перетворіть його в рядок шаблону

let b=10;
console.log(a.template());//b:10

без eval , new Functionі інші засоби динамічної генерації коду?


5
ти знайшов спосіб досягти цього? Мені може знадобитися зробити це одного дня і мені цікаво дізнатися, до чого ти приїхав.
Брайан Рейнер

@BryanRayner дозволяє сказати, що ваша програма js намагається отримати дані з API відпочинку, URL-адреса якого знаходиться у файлі config.js як рядок "/ Resources / <resource_id> / update /", і ви динамічно ставите "resource_id" зі своєї програми . Якщо ви не хочете розділити цей URL на частини і зберегти в різних областях, вам потрібна якась обробка шаблону рядків.
Ryu_hayabusa


Замість використання eval краще використовувати для регулярного виведення Eval це не рекомендується та сильно не відволікає, тому, будь ласка, не використовуйте це developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…! нехай b = 10; нехай a = "b: $ {b}"; нехай відповідь = a.замінити (/ \ $ {\ w +} /, b); conssole.log (відповідь);
Віджай Паласкар

Відповіді:


79

Оскільки ваш рядок шаблону повинен bдинамічно посилатися на змінну (під час виконання), то відповідь така: НІ, неможливо обійтися без генерації динамічного коду.

Але evalце досить просто:

let tpl = eval('`'+a+'`');

7
eval є незахищеним, як і інші засоби генерації динамічного коду
KOLANICH

8
@KOLANICH Для Зокрема , що випадок - втеча назад лапки в aрядок , і це буде набагато менш небезпечно: let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');. Думаю, важливішим є те, що evalзапобігти компілятору оптимізувати ваш код. Але я вважаю, що це питання не має значення.
алексподи

3
Насправді ви також можете запускати функції всередині рядків шаблону.
КОЛАНИЧ

9
@KOLANICH Вибачте, що вам не подобається eval. Однак пам’ятайте, що буквальний шаблон сам по собі є формою eval. Два приклади: var test = Result: ${alert('hello')}; вар тест = Result: ${b=4}; Обидва закінчать виконання довільного коду в контексті сценарію. Якщо ви хочете дозволити довільні рядки, ви можете також дозволити eval.
Манго

6
Будь обережний. Оскільки щось на кшталт babel цього не перекладе, цей код НЕ буде працювати в IE
cgsd

79

У своєму проекті я створив щось подібне з ES6:

String.prototype.interpolate = function(params) {
  const names = Object.keys(params);
  const vals = Object.values(params);
  return new Function(...names, `return \`${this}\`;`)(...vals);
}

const template = 'Example text: ${text}';
const result = template.interpolate({
  text: 'Foo Boo'
});
console.log(result);

ОНОВЛЕННЯ Я видалив залежність лодашу, ES6 має еквівалентні методи отримання ключів та значень.


1
Привіт, ваше рішення працює чудово, але коли я використовував його в React Native (режим складання), він видає помилку: Недійсний символ '' ' , хоча він працює, коли я запускаю в режимі налагодження. Виглядає, питання про дівоцтво, допомогу?
Мохіт Панді

@MohitPandey У мене виникла така ж помилка, коли я виконував тести цього коду під PhantomJS, і він проходив під хромом. Якщо це так, я думаю, що на шляху з'явилася нова бета-версія PhantomJS з кращою підтримкою ES6, ви можете спробувати її встановити.
Матеуш Моска

1
На жаль, це не працює, і я записав регекс для того ж. Додано також як відповідь.
Мохіт Пандей

це рішення працює лише в тому випадку, якщо символу "" "зворотного галочки немає в рядку шаблону
SliverNinja - MSFT

Коли я спробую це, я отримав ReferenceError: _ is not defined. Це код не ES6, а lodashконкретний, або ...?
xpt

29

Що ви тут просите:

//non working code quoted from the question
let b=10;
console.log(a.template());//b:10

абсолютно рівнозначний (з точки зору потужності та, ер, безпеки) eval: здатності взяти рядок, що містить код, і виконати цей код; а також можливість виконаного коду бачити локальні змінні в середовищі абонента.

У JS немає можливості функції бачити локальні змінні у своєму виклику, якщо тільки ця функція не є eval(). Навіть Function()не може це зробити.


Коли ви чуєте, що в JavaScript йде щось, що називається "рядки шаблону", природно припустити, що це вбудована бібліотека шаблонів, як Вуса. Це не так. В основному це просто інтерполяція рядків і багаторядкових рядків для JS. Я думаю, що це на певний час стане поширеною помилкою. :(


2
ТБХ це те, що я думав, що це було. Було б дуже, дуже зручно.
Брайан Рейнер

Це (досі) працює? Я отримую template is not a function.
Ionică Bizău

2
Блок коду вгорі цієї відповіді - це цитата з питання. Це не працює.
Джейсон Орендорф

27

Ні, немає способу зробити це без динамічної генерації коду.

Однак я створив функцію, яка перетворить звичайну рядок у функцію, якій можна забезпечити карту значень, використовуючи внутрішньо шаблонні рядки.

Створення рядка рядків шаблону

/**
 * Produces a function which uses template strings to do simple interpolation from objects.
 * 
 * Usage:
 *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
 * 
 *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
 *    // Logs 'Bryan is now the king of Scotland!'
 */
var generateTemplateString = (function(){
    var cache = {};

    function generateTemplate(template){
        var fn = cache[template];

        if (!fn){
            // Replace ${expressions} (etc) with ${map.expressions}.

            var sanitized = template
                .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                    return `\$\{map.${match.trim()}\}`;
                    })
                // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

            fn = Function('map', `return \`${sanitized}\``);
        }

        return fn;
    }

    return generateTemplate;
})();

Використання:

var kingMaker = generateTemplateString('${name} is king!');

console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.

Сподіваюся, це комусь допомагає. Якщо ви знайшли проблему з кодом, будь ласка, будьте ласкаві, щоб оновити історію.


Дякую! Я використовував це замість рішення sprintf javascript.
seangwright

1
працює не для кожного var test = generateTemplateString('/api/${param1}/${param2}/') console.log(test({param1: 'bar', param2: 'foo'}))повернення шаблонів/api/bar//
Гійом Вінсент

Спасибі, виправлено. Регекс включав один матч $ {param1} / $ {param2}, коли це повинно було бути два матчі.
Брайан Рейнер

Зауважте, що ця програма не працює в IE11 через відсутність підтримки зворотних кліщів.
s.meijer

1
Звичайно, якщо рядки шаблону не підтримуються браузером, цей метод не працюватиме. Якщо ви хочете використовувати рядки шаблонів у непідтримуваних веб-переглядачах, я рекомендую використовувати таку мову, як TypeScript або транспілер, як Babel; Це єдиний спосіб ввести ES6 у старі браузери.
Брайан Рейнер

9

TLDR: https://jsfiddle.net/w3jx07vt/

Усі, здається, переживають за доступ до змінних, чому б просто не передати їх? Я впевнений, що не буде занадто важко отримати змінний контекст у абонента та передати його. Скористайтеся цим https://stackoverflow.com/a/6394168/6563504, щоб отримати реквізит від obj. Я зараз не можу перевірити вас, але це має працювати.

function renderString(str,obj){
    return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}

Випробуваний. Ось повний код.

function index(obj,is,value) {
    if (typeof is == 'string')
        is=is.split('.');
    if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

function renderString(str,obj){
    return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)})
}

renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas

@ s.meijer ви могли б детальніше розглянути? Я успішно використовую цей код. jsfiddle.net/w3jx07vt
M3D

1
Краще виразка дозволить вам не вибирати ${}символи. Спробуйте:/(?!\${)([^{}]*)(?=})/g
Ерік Ходонський

@Relic jsfiddle.net/w3jx07vt/2 Я не міг так працювати, дотримуючись руку, і я оновлю свою посаду? :)
M3D

Тож те, що ви намагаєтеся схопити це, насправді не дуже допоможе, я замість цього зробив заміну рядка. Замість того, щоб додавати крок інтерполяції, я можу використовувати рядок як інтерп або рядок. Не фантазії, але це спрацювало.
Ерік Ходонський

8

Проблема тут полягає у тому, щоб функція мала доступ до змінних свого виклику. Ось чому ми бачимо, що прямий evalвикористовується для обробки шаблонів. Можливим рішенням було б генерувати функцію, яка приймає формальні параметри, названі властивостями словника, і називає її відповідними значеннями в тому ж порядку. Альтернативним способом було б мати щось просте, як це:

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

І для всіх, хто використовує компілятор Babel, нам потрібно створити закриття, яке запам'ятовує середовище, в якому він був створений:

console.log(new Function('name', 'return `' + message + '`;')(name));

Ваш перший фрагмент насправді гірший, ніж evalтому, що він працює лише із глобальною nameзмінною
Бергі

@Bergi Ваше твердження дійсне - область функції буде втрачена. Я хотів представити просте вирішення проблеми і подав спрощений приклад того, що можна зробити. Можна просто придумати таке, щоб подолати проблему: var template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return '+ message +';')();}
didinko

Ні, це саме те , що б НЕ робота - new Functionне має доступу до var nameв templateфункції.
Бергі

Другий знімок вирішив мою проблему ... Голосуй від мене! Дякую, це допомогло вирішити тимчасову проблему, з якою ми стикалися з динамічною маршрутизацією до iframe :)
Kris Boyd

7

Тут розміщено багато хороших рішень, але жодне ще не використовувало метод ES6 String.raw . Ось моя сутичка. Це важливе обмеження в тому, що він приймає лише властивості, передані в об'єкт, тобто виконання коду в шаблоні не буде працювати.

function parseStringTemplate(str, obj) {
    let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/);
    let args = str.match(/[^{\}]+(?=})/g) || [];
    let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
    return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };

parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
  1. Розбийте рядок на текстові частини без аргументу. Див. Регекс .
    parts: ["Hello, ", "! Are you ", " years old?"]
  2. Розбийте рядок на назви властивостей. Порожній масив, якщо збіг не вдається.
    args: ["name", "age"]
  3. Параметри карти від objназви властивості Рішення обмежується неглибоким відображенням на одному рівні. Не визначені значення замінюються порожнім рядком, але інші помилкові значення приймаються.
    parameters: ["John Doe", 18]
  4. Використовуйте String.raw(...)і повертайте результат.

Яке значення насправді надає String.raw з цікавості? Здається, ви виконуєте всю роботу з розбору рядка та відстеження, що таке підстанції. Чи багато чого це відрізняється від простого .replace()повторного дзвінка ?
Стів Беннетт

Справедлива точка, @SteveBennett. У мене виникли проблеми з перетворенням звичайної рядка в шаблон-рядок, і я знайшов рішення, побудувавши сам необроблений об'єкт. Я думаю, що це зводить String.raw до методу конкатенації, але я думаю, що він працює досить добре. Хотілося б побачити гарне рішення .replace(), хоча :) Я вважаю, що читабельність важлива, тому, використовуючи самі регулярні вирази, я намагаюся назвати їх, щоб допомогти зрозуміти це все ...
pekaaw

6

Схожий на відповідь Даніеля (і суть s.meijer ), але більш зрозумілий:

const regex = /\${[^{]+}/g;

export default function interpolate(template, variables, fallback) {
    return template.replace(regex, (match) => {
        const path = match.slice(2, -1).trim();
        return getObjPath(path, variables, fallback);
    });
}

//get the specified property or nested property of an object
function getObjPath(path, obj, fallback = '') {
    return path.split('.').reduce((res, key) => res[key] || fallback, obj);
}

Примітка. Це трохи покращує оригінал s.meijer, оскільки він не збігається з такими, як ${foo{bar}(наприклад, регулярний вираз дозволяє лише символи не фігурних дужок всередині ${та }).


ОНОВЛЕННЯ: Мене попросили приклад, використовуючи це, тож ось вам:

const replacements = {
    name: 'Bob',
    age: 37
}

interpolate('My name is ${name}, and I am ${age}.', replacements)

Чи можете ви розмістити приклад, фактично використовуючи це? Цей javascript трохи поза мене. Я б запропонував регулярний вираз /\$\{(.*?)(?!\$\{)\}/g(для обробки фігурних дужок гнізд). У мене є робоче рішення, але я не впевнений, що він настільки портативний, як ваш, тому я хотів би побачити, як це має бути реалізовано на сторінці. Шахта також використовує eval().
Регулярний Джо

Я продовжив і опублікував відповідь, і мені б хотілося, щоб ваш відгук про те, як зробити це більш безпечним та ефективним: stackoverflow.com/a/48294208 .
Регулярний Джо

@RegularJoe Я додав приклад. Моя мета полягала в тому, щоб зробити це просто, але ви праві, що якщо ви хочете обробити вкладені фігурні дужки, вам потрібно буде змінити регулярний вираз. Однак я не можу придумати випадок використання для цього, коли оцінювати звичайний рядок так, як ніби це буквальний шаблон (уся мета цієї функції). Що ви мали на увазі?
Метт Браун

Крім того, я не є ні експертом з ефективності, ні експертом з питань безпеки; моя відповідь насправді просто поєднує дві попередні відповіді. Але я скажу, що використання evalзалишає вас набагато більш відкритими до можливих помилок, які можуть спричинити проблеми із безпекою, тоді як у всіх моїх версіях є пошук властивості на об'єкті з точки, розділеної крапками, яка повинна бути безпечною.
Метт Браун

5

Ви можете використовувати прототип протоколу, наприклад

String.prototype.toTemplate=function(){
    return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10

Але відповісти на початкове запитання - ніяк.


5

Мені сподобалась відповідь s.meijer і я написав власну версію на основі його:

function parseTemplate(template, map, fallback) {
    return template.replace(/\$\{[^}]+\}/g, (match) => 
        match
            .slice(2, -1)
            .trim()
            .split(".")
            .reduce(
                (searchObject, key) => searchObject[key] || fallback || match,
                map
            )
    );
}

1
Акуратно! Дійсно акуратний!
xpt

4

Мені потрібен цей метод із підтримкою Internet Explorer. Виявилося, що задні кліщі не підтримуються навіть IE11. Також; використання evalабо його еквівалентFunction не відчуває себе правильно.

Для того, що помічає; Я також використовую backticks, але вони видаляються компіляторами, як babel. Методи, запропоновані іншими, залежать від них від часу виконання. Як було сказано раніше; це проблема в IE11 і нижче.

Тож ось що я придумав:

function get(path, obj, fb = `$\{${path}}`) {
  return path.split('.').reduce((res, key) => res[key] || fb, obj);
}

function parseTpl(template, map, fallback) {
  return template.replace(/\$\{.+?}/g, (match) => {
    const path = match.substr(2, match.length - 3).trim();
    return get(path, map, fallback);
  });
}

Приклад виводу:

const data = { person: { name: 'John', age: 18 } };

parseTpl('Hi ${person.name} (${person.age})', data);
// output: Hi John (18)

parseTpl('Hello ${person.name} from ${person.city}', data);
// output: Hello John from ${person.city}

parseTpl('Hello ${person.name} from ${person.city}', data, '-');
// output: Hello John from -

"використання eval або його еквівалентної Функції не вірно." ... Так ... Я згоден, але я думаю, що це один із небагатьох випадків використання, в якому можна було б сказати: "ммм-хай, давайте використовувати це". Перевірте jsperf.com/es6-string-tmpl - це мій практичний випадок використання. Використовуючи свою функцію (з тим же regexp, як і моя) та mine (eval + string literals). Дякую! :)
Андреа Пудду,

@AndreaPuddu, ваша ефективність справді краща. Але потім знову; Рядки шаблону не підтримуються в IE. Так eval('`' + taggedURL + '`')просто не працює.
s.meijer

Краще я б сказав, "тому що він перевірений ізольовано ... Єдина мета цього тесту полягала в тому, щоб побачити потенційні проблеми з використанням продуктивності eval. Щодо літеральних шаблонів: дякую, що ще раз вказали на це. Я використовую Babel, щоб перекласти свій код, але функція все одно не буде працювати, мабуть, 😐
Андреа

3

Наразі я не можу коментувати існуючі відповіді, тому я не в змозі прямо коментувати відмінну відповідь Брайана Рейнора. Таким чином, ця відповідь збирається оновити свою відповідь з невеликою корекцією.

Коротше кажучи, його функція насправді не може кешувати створену функцію, тому вона завжди буде відтворена, незалежно від того, чи бачив шаблон раніше. Ось виправлений код:

    /**
     * Produces a function which uses template strings to do simple interpolation from objects.
     * 
     * Usage:
     *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
     * 
     *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
     *    // Logs 'Bryan is now the king of Scotland!'
     */
    var generateTemplateString = (function(){
        var cache = {};

        function generateTemplate(template){
            var fn = cache[template];

            if (!fn){
                // Replace ${expressions} (etc) with ${map.expressions}.

                var sanitized = template
                    .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                        return `\$\{map.${match.trim()}\}`;
                    })
                    // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                    .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

                fn = cache[template] = Function('map', `return \`${sanitized}\``);
            }

            return fn;
        };

        return generateTemplate;
    })();

3

@Mateusz Moska, рішення відмінно працює, але коли я використовував його в React Native (режим складання), він видає помилку: Недійсний символ '' ' , хоча він працює, коли я запускаю його в режимі налагодження.

Тому я записав власне рішення за допомогою регексу.

String.prototype.interpolate = function(params) {
  let template = this
  for (let key in params) {
    template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key])
  }
  return template
}

const template = 'Example text: ${text}',
  result = template.interpolate({
    text: 'Foo Boo'
  })

console.log(result)

Демо: https://es6console.com/j31pqx1p/

ПРИМІТКА. Оскільки я не знаю першопричини проблеми, я підняв квиток у рідному репо, https://github.com/facebook/react-native/isissue/14107 , так що колись вони зможуть виправити / направити мене приблизно те саме :)


це робить шаблони підтримки, що містять символ зворотного галочки. Однак, замість того, щоб намагатися вигадувати шаблонний шаблон, вам, мабуть, краще просто використовувати вуса або подібні . Залежно від того, наскільки складні ваші шаблони, це підхід грубої сили, який не враховує крайових випадків - ключ може містити спеціальну схему зворотного вираження.
SliverNinja - MSFT

2

Досі динамічний, але здається більш контрольованим, ніж просто використання голого eval:

const vm = require('vm')
const moment = require('moment')


let template = '### ${context.hours_worked[0].value} \n Hours worked \n #### ${Math.abs(context.hours_worked_avg_diff[0].value)}% ${fns.gt0(context.hours_worked_avg_diff[0].value, "more", "less")} than usual on ${fns.getDOW(new Date())}'
let context = {
  hours_worked:[{value:10}],
  hours_worked_avg_diff:[{value:10}],

}


function getDOW(now) {
  return moment(now).locale('es').format('dddd')
}

function gt0(_in, tVal, fVal) {
  return _in >0 ? tVal: fVal
}



function templateIt(context, template) {
  const script = new vm.Script('`'+template+'`')
  return script.runInNewContext({context, fns:{getDOW, gt0 }})
}

console.log(templateIt(context, template))

https://repl.it/IdVt/3


1

Це рішення працює без ES6:

function render(template, opts) {
  return new Function(
    'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' +
    ').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');'
  )();
}

render("hello ${ name }", {name:'mo'}); // "hello mo"

Примітка: Functionконструктор завжди створюється в глобальному масштабі, що потенційно може спричинити перезапис глобальних змінних шаблоном, наприкладrender("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});


0

Оскільки ми винаходимо колесо на чомусь, що було б чудовою особливістю у JavaScript.

Я використовую eval(), що не захищено, але JavaScript не захищено. Я легко визнаю, що я не чудовий в JavaScript, але в мене була потреба, і мені потрібна відповідь, тому я зробив її.

Я вирішив стилізувати свої змінні @замість, а не $, особливо тому, що хочу використовувати безлінійну функцію літералів без оцінюючи, поки вона не готова. Отже синтаксис змінної є@{OptionalObject.OptionalObjectN.VARIABLE_NAME}

Я не фахівець з javascript, тому я б радий порадити щодо вдосконалення, але ...

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.length; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

Далі слід дуже проста реалізація

myResultSet = {totalrecords: 2,
Name: ["Bob", "Stephanie"],
Age: [37,22]};

rt = `My name is @{myResultSet.Name}, and I am @{myResultSet.Age}.`

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.totalrecords; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

У своїй реальній реалізації я вирішую використовувати @{{variable}}. Ще один набір брекетів. Абсурдно навряд чи зіткнуться з цим несподівано. Регекс для цього виглядав би так/\@\{\{(.*?)(?!\@\{\{)\}\}/g

Щоб зробити це легше читати

\@\{\{    # opening sequence, @{{ literally.
(.*?)     # capturing the variable name
          # ^ captures only until it reaches the closing sequence
(?!       # negative lookahead, making sure the following
          # ^ pattern is not found ahead of the current character
  \@\{\{  # same as opening sequence, if you change that, change this
)
\}\}      # closing sequence.

Якщо ви не стикалися з регулярним виразом, досить безпечне правило , щоб уникнути все не алфавітно-цифровий символ, а не коли - небудь без необхідності уникнути букв , як багато вцілілі листи мають особливе значення для практично всіх різновидів регулярних виразів.


0

Спробуйте спробувати цей крихітний модуль JS від Андреа Джаммарчі з github: https://github.com/WebReflection/backtick-template

/*! (C) 2017 Andrea Giammarchi - MIT Style License */
function template(fn, $str, $object) {'use strict';
  var
    stringify = JSON.stringify,
    hasTransformer = typeof fn === 'function',
    str = hasTransformer ? $str : fn,
    object = hasTransformer ? $object : $str,
    i = 0, length = str.length,
    strings = i < length ? [] : ['""'],
    values = hasTransformer ? [] : strings,
    open, close, counter
  ;
  while (i < length) {
    open = str.indexOf('${', i);
    if (-1 < open) {
      strings.push(stringify(str.slice(i, open)));
      open += 2;
      close = open;
      counter = 1;
      while (close < length) {
        switch (str.charAt(close++)) {
          case '}': counter -= 1; break;
          case '{': counter += 1; break;
        }
        if (counter < 1) {
          values.push('(' + str.slice(open, close - 1) + ')');
          break;
        }
      }
      i = close;
    } else {
      strings.push(stringify(str.slice(i)));
      i = length;
    }
  }
  if (hasTransformer) {
    str = 'function' + (Math.random() * 1e5 | 0);
    if (strings.length === values.length) strings.push('""');
    strings = [
      str,
      'with(this)return ' + str + '([' + strings + ']' + (
        values.length ? (',' + values.join(',')) : ''
      ) + ')'
    ];
  } else {
    strings = ['with(this)return ' + strings.join('+')];
  }
  return Function.apply(null, strings).apply(
    object,
    hasTransformer ? [fn] : []
  );
}

template.asMethod = function (fn, object) {'use strict';
  return typeof fn === 'function' ?
    template(fn, this, object) :
    template(this, fn);
};

Демо (усі наступні тести повертаються вірно):

const info = 'template';
// just string
`some ${info}` === template('some ${info}', {info});

// passing through a transformer
transform `some ${info}` === template(transform, 'some ${info}', {info});

// using it as String method
String.prototype.template = template.asMethod;

`some ${info}` === 'some ${info}'.template({info});

transform `some ${info}` === 'some ${info}'.template(transform, {info});

0

Я зробив власне рішення, роблячи тип з описом як функцією

export class Foo {
...
description?: Object;
...
}

let myFoo:Foo = {
...
  description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}

і так робимо:

let myDescription = myFoo.description('Bar', 'bar');

0

Замість використання eval краще - використовувати для регулярного виведення

Eval це не рекомендується і сильно не рекомендується використовувати його, тому не використовуйте його ( mdn eval ).

 let b = 10;
 let a="b:${b}";

let response = a.replace(/\${\w+}/ ,b);
conssole.log(response);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.