1import{t as n,a as e}from"../chunks/BRuBKdOt.js";import"../chunks/B4ndRVNs.js";import{e as t}from"../chunks/D3qO8zUI.js";var l=n(`<p>A new-ish feature of JavaScript is the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">Template Literal</a>. It's basically a fancy string that lets you interpolate stuff and use newlines. But template literals have another feature that isn't as well-known, probably because they're a little weird and hard to find use cases for.</p> <p>That feature is the "tagged" template. Below I highlight my favorite use case for tagged templates: preventing strings from making your code look hideous.</p> <h2 id="word-wrap-long-strings"><a aria-hidden="true" tabindex="-1" href="#word-wrap-long-strings"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Word-wrap long strings</h2> <p>Everyone says to limit how many characters appear in in each row of your code. This keep-it-short directive is a problem for one kind of data in particular: strings. A single str
1ing literal can be longer than those 80 characters all by itself, and you can't refactor that length away.</p> <p>So what do you do?</p> <p>The classic solution looks like this:</p> <pre><code class="hljs language-js"><span class="hljs-keyword">const</span> myString = 2 <span class="hljs-string">'Oh how I wish '</span> + 3 <span class="hljs-string">'that this string was shorter '</span> + 4 <span class="hljs-string">"so that I didn't have to break "</span> + 5 <span class="hljs-string">'it up like this. I mean seriously '</span> + 6 <span class="hljs-string">'look how hard it is to read! '</span> + 7 <span class="hljs-string">'and edit!'</span>; 8</code></pre> <p>Hideous.</p> <p>This is something we can dramatically improve with a tagged template. I've made a tag called <code>oneline</code> that lets you use as many line breaks and as much indentation as you want, while still ending up with a oneline string! (For the <code>oneline</code> code see the bottom of this article.)</p> <pre><code class="hljs language-js"><span class="hljs-keyword">const</span> myString = oneline<span class="hljs-string">\` 9 When you console log this it will just 10 be one long line. It looks like a bunch. 11 But it's just one. 12 I can even ident the rows. 13 I could have started the first 14 line right after that first tick, 15 and can put the final tick on its 16 own line or at the end of this one. 17 \`</span>; 18</code></pre> <p>While that string <em>looks</em> like it has a bunch of newlines and tabs in it, it actually doesn't. What you see is not what you get with tagged templates. Here, what you actually get is that <code>myString</code> is one long string without extra spaces or newlines.</p> <h2 id="match-code-indentation-with-multiline-strings"><a aria-hidden="true" tabindex="-1" href="#match-code-indentation-with-multiline-strings"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Match code indentation with multiline strings</h2> <p>There is another common problem with strings in code: indentation in multiline strings. Template literals allow us to put breaks in our strings (without having to explicitly type out <code>\\n</code>), giving us two options out of the gate for dealing with newlines and indentation:</p> <pre><code class="hljs language-js"><span class="hljs-keyword">const</span> myString = 19 <span class="hljs-string">'Title\\n'</span> + <span class="hljs-string">' Indented line 1\\n'</span> + <span class="hljs-string">' Indented line 2\\n Indented Line 3\\n'</span>; 20 21<span class="hljs-keyword">const</span> myString = <span class="hljs-string">\`Title 22 Indented Line 1 23 Indented Line 2 24 Indented Line 3 25\`</span>; 26</code></pre> <p>Hey, that template literal doesn't look bad at all, does it! But how about now:</p> <pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">myFunction</span>(<span class="hljs-params"></span>) { 27 <span class="hljs-keyword">let</span> myString; 28 <span class="hljs-keyword">if</span> (someCondition) { 29 <span class="hljs-keyword">if</span> (someOtherCondition) { 30 myString = <span class="hljs-string">\`Title 31 Indented Line 1 32 Indented Line 2 33 Indented Line 3 34\`</span>; 35 } 36 } 37} 38</code></pre> <p>Gross. Since the template literal uses the whitespace exactly as typed, your multiline string cannot adhere to the indentation of the code in its context. That is, unless we use a tagged template. I've made a tag called <code>undent</code> that lets you write multiline strings while adhering to the indentation of the code context. I call it "undent" because it removes the superfluous indentation. (For the <code>undent</code> code see the bottom of this article.)</p> <pre><code class="hljs language-js"><span class="hljs-comment">// Same *result* as before, but allowing indentation to match the code.</sp
38an> 39<span class="hljs-keyword">function</span> <span class="hljs-title function_">myFunction</span>(<span class="hljs-params"></span>) { 40 <span class="hljs-keyword">let</span> myString; 41 <span class="hljs-keyword">if</span> (someCondition) { 42 <span class="hljs-keyword">if</span> (someOtherCondition) { 43 myString = undent<span class="hljs-string">\` 44 Title 45 Indented Line 1 46 Indented Line 2 47 Indented Line 3 48 \`</span>; 49 } 50 } 51} 52</code></pre> <h2 id="how-it-works"><a aria-hidden="true" tabindex="-1" href="#how-it-works"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>How it works</h2> <p>Template tags are regular old functions that receive the contents of the template literal as a bunch of strings and the interpolated values. If you stitch these together, you get the whole string. The tag function lets you do stuff <em>before</em> stitching all of that together if you want.</p> <p>Here's the code (in Typescript):</p> <pre><code class="hljs language-ts"><span class="hljs-comment">/** <span class="hljs-doctag">@file</span> https://github.com/bscotch/node-util/blob/main/src/lib/strings.ts **/</span> 53 54<span class="hljs-comment">/** 55 * Concatenate the string fragments and interpolated values 56 * to get a single string. 57 */</span> 58<span class="hljs-keyword">function</span> <span class="hljs-title function_">populateTemplate</span>(<span class="hljs-params"><span class="hljs-attr">strings</span>: <span class="hljs-title class_">TemplateStringsArray</span>, ...<span class="hljs-attr">interps</span>: <span class="hljs-built_in">string</span>[]</span>) { 59 <span class="hljs-keyword">let</span> <span class="hljs-built_in">string</span> = <span class="hljs-string">''</span>; 60 <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < strings.<span class="hljs-property">length</span>; i++) { 61 <span class="hljs-built_in">string</span> += <span class="hljs-string">\`<span class="hljs-subst">${strings[i] || <span class="hljs-string">''</span>}</span><span class="hljs-subst">${interps[i] || <span class="hljs-string">''</span>}</span>\`</span>; 62 } 63 <span class="hljs-keyword">return</span> <span class="hljs-built_in">string</span>; 64} 65 66<span class="hljs-comment">/** 67 * Shift all lines left by the *smallest* indentation level, 68 * and remove initial newline and all trailing spaces. 69 */</span> 70<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">undent</span>(<span class="hljs-params"><span class="hljs-attr">strings</span>: <span class="hljs-title class_">TemplateStringsArray</span>, ...<span class="hljs-attr">interps</span>: <span class="hljs-built_in">string</span>[]</span>) { 71 <span class="hljs-keyword">let</span> <span class="hljs-built_in">string</span> = <span class="hljs-title function_">populateTemplate</span>(strings, ...interps); 72 <span class="hljs-comment">// Remove initial and final newlines</span> 73 <span class="hljs-built_in">string</span> = <span class="hljs-built_in">string</span>.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/^[\\r\\n]+/</span>, <span class="hljs-string">''</span>).<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\\s+$/</span>, <span class="hljs-string">''</span>); 74 <span class="hljs-keyword">const</span> dents = <span class="hljs-built_in">string</span>.<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^([ \\t])*/gm</span>); 75 <span class="hljs-keyword">if</span> (!dents || dents.<span class="hljs-property">length</span> == <span class="hljs-number">0</span>) { 76 <span class="hljs-keyword">return</span> <span class="hljs-built_in">string</span>; 77 } 78 dents.<span class="hljs-title function_">
78sort</span>(<span class="hljs-function">(<span class="hljs-params">dent1, dent2</span>) =></span> dent1.<span class="hljs-property">length</span> - dent2.<span class="hljs-property">length</span>); 79 <span class="hljs-keyword">const</span> minDent = dents[<span class="hljs-number">0</span>]; 80 <span class="hljs-keyword">if</span> (!minDent) { 81 <span class="hljs-comment">// Then min indentation is 0, no change needed</span> 82 <span class="hljs-keyword">return</span> <span class="hljs-built_in">string</span>; 83 } 84 <span class="hljs-keyword">const</span> dedented = <span class="hljs-built_in">string</span>.<span class="hljs-title function_">replace</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">RegExp</span>(<span class="hljs-string">\`^<span class="hljs-subst">${minDent}</span>\`</span>, <span class="hljs-string">'gm'</span>), <span class="hljs-string">''</span>); 85 <span class="hljs-keyword">return</span> dedented; 86} 87 88<span class="hljs-comment">/** 89 * Remove linebreaks and extra spacing in a template string. 90 */</span> 91<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">oneline</span>(<span class="hljs-params"><span class="hljs-attr">strings</span>: <span class="hljs-title class_">TemplateStringsArray</span>, ...<span class="hljs-attr">interps</span>: <span class="hljs-built_in">string</span>[]</span>) { 92 <span class="hljs-keyword">return</span> <span class="hljs-title function_">populateTemplate</span>(strings, ...interps) 93 .<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/^\\s+/</span>, <span class="hljs-string">''</span>) 94 .<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\\s+$/</span>, <span class="hljs-string">''</span>) 95 .<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\\s+/g</span>, <span class="hljs-string">' '</span>); 96} 97</code></pre> <p>I've included these template tags in <a href="https://www.npmjs.com/package/@bscotch/utility">a node utility module</a> I use in most of my projects. That's a quick way to try these out.</p> <p>I'd love to hear about your favorite use cases for tagged templates!</p> <p><em>Join the <a href="https://tinybs.co/devchat">DevChat newsletter</a> to get more stuff like this, plus discussions of entrepreneurship and gamedev.</em></p>`,1);function o(s){var a=l();t(48),e(s,a)}export{o as component};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.