Polyglot
Languages TypeScript strings
TypeScript § strings

Strings

TypeScript inherits JavaScript’s string type — an immutable sequence of UTF-16 code units, supporting Unicode through its full code point range. The conventional string forms are single-quoted, double-quoted, and template literals (backtick-delimited, admitting interpolation and multi-line content). TypeScript adds literal string types (typed as a specific string value) and template literal types (computed string-typed unions). The combination — JavaScript’s string operations and the TypeScript-specific string types — covers the routine text-processing surface.

String literals

Three forms:

const a = 'single quotes';
const b = "double quotes";
const c = `template literal`;

// Escape sequences:
const d = "line one\nline two";
const e = 'tab:\there';
const f = "quote: \"inner\"";
const g = "backslash: \\";

// Unicode:
const h = "é";                              // é
const i = "\u{1F600}";                           // 😀 (since ES2015)

// Template literals:
const name = "Alice";
const greeting = `Hello, ${name}!`;
const expr = `Sum: ${1 + 2}`;
const multiline = `
    line 1
    line 2
`;

The conventional contemporary style:

  • Use single quotes — slightly more conventional than double quotes.
  • Use template literals for interpolation and multi-line strings.
  • Avoid double quotes in code (preferred for JSON).

Template literals

The backtick form admits embedded expressions:

const name = "Alice";
const age = 30;

const greeting = `Hello, ${name}! You are ${age} years old.`;

// Expression of any complexity:
const formatted = `Pi is approximately ${Math.PI.toFixed(2)}`;
const result = `${a > b ? "first" : "second"} is larger`;

// Nested:
const outer = `outer: ${`inner: ${42}`}`;

// Multi-line (preserves all whitespace):
const sql = `
    SELECT id, name
    FROM users
    WHERE active = true
`;

The mechanism admits substantial conciseness for formatting; conventional for log messages, SQL, HTML templates, and similar contexts.

Tagged templates

A tagged template applies a function to the parts of a template literal:

function highlight(strings: TemplateStringsArray, ...values: unknown[]): string {
    let result = "";
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            result += `<mark>${values[i]}</mark>`;
        }
    }
    return result;
}

const html = highlight`Hello, ${name}! You are ${age} years old.`;
// "Hello, <mark>Alice</mark>! You are <mark>30</mark> years old."

The conventional uses are CSS-in-JS libraries, SQL builders, and i18n systems.

String operations

Common string methods:

const s = "hello world";

s.length;                                         // 11
s[0];                                             // "h" (string of length 1)
s.charAt(0);                                      // "h"
s.charCodeAt(0);                                  // 104
s.codePointAt(0);                                 // 104 (admits surrogate pairs)

s.includes("world");                             // true
s.startsWith("hello");                           // true
s.endsWith("world");                             // true
s.indexOf("world");                              // 6
s.lastIndexOf("o");                              // 7

s.toUpperCase();                                  // "HELLO WORLD"
s.toLowerCase();                                  // "hello world"
s.trim();                                         // strip whitespace from both ends
s.trimStart();
s.trimEnd();

s.slice(0, 5);                                    // "hello"
s.slice(-5);                                      // "world"
s.substring(0, 5);                                // "hello" (similar to slice; differs on negative)
s.split(" ");                                     // ["hello", "world"]
s.split("");                                      // ["h", "e", "l", "l", "o", ...]

s.replace("world", "TS");                         // "hello TS"
s.replaceAll("l", "L");                           // "heLLo worLd"

s.padStart(15, "*");                              // "****hello world"
s.padEnd(15, "*");                                // "hello world****"

s.repeat(3);                                      // "hello worldhello worldhello world"

s.normalize("NFC");                               // Unicode normalisation

For substring search with regex, the match and replace methods accept patterns:

const matches = "abc 123 def 456".match(/\d+/g);  // ["123", "456"]
const replaced = "abc 123".replace(/\d+/g, "X");  // "abc X"

String iteration

A string admits iteration via for ... of (which yields Unicode code points):

const s = "héllo";

for (let i = 0; i < s.length; i++) {
    console.log(s.charCodeAt(i));                 // UTF-16 code units
}

for (const c of s) {
    console.log(c);                               // Unicode code points (handles surrogate pairs)
}

const chars = [...s];                             // ["h", "é", "l", "l", "o"]
const charsArray = Array.from(s);                 // same

A subtle point: the length property and indexed access work on UTF-16 code units, not code points. Characters outside the Basic Multilingual Plane (e.g., emoji) consist of two UTF-16 code units (a surrogate pair):

const emoji = "😀";
console.log(emoji.length);                        // 2 (two code units)
console.log([...emoji].length);                   // 1 (one code point)

The for ... of loop and the spread operator handle code points correctly; raw indexing does not.

String types

TypeScript’s principal string-related types:

string

The general string type:

let s: string = "hello";

Literal string types

let greeting: "hello" = "hello";

type Status = "active" | "inactive" | "banned";
let status: Status = "active";

The conventional uses are enum-like unions and discriminated unions.

Template literal types

Since TS 4.1, template literal types admit compile-time string manipulation:

type Greeting = `Hello, ${string}!`;
type World = `${"morning" | "afternoon" | "evening"}`;
type Joined = `${"a" | "b"}-${"x" | "y"}`;       // "a-x" | "a-y" | "b-x" | "b-y"

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">;            // "onClick"

// Common pattern: extract from a templated type:
type ExtractParam<T extends string> =
    T extends `${string}{${infer P}}${string}` ? P : never;

type Param = ExtractParam<"GET /users/{id}">;    // "id"

The mechanism admits substantial type-level computation; treated in Advanced types.

Built-in string utility types

TypeScript provides string-manipulation utility types:

type Upper = Uppercase<"hello">;                 // "HELLO"
type Lower = Lowercase<"HELLO">;                 // "hello"
type Cap = Capitalize<"hello">;                  // "Hello"
type Uncap = Uncapitalize<"Hello">;              // "hello"

The utilities work at the type level only; for runtime equivalents, use the string methods.

Conversion to/from string

// Number to string:
const a = String(42);                            // "42"
const b = (42).toString();
const c = `${42}`;                               // template literal coercion
const d = (3.14).toFixed(2);                     // "3.14"
const e = (255).toString(16);                    // "ff" (hex)

// String to number:
const n = Number("42");                          // 42
const f = parseFloat("3.14");                    // 3.14
const i = parseInt("42", 10);                    // 42 (base 10)
const j = +"42";                                 // 42 (unary plus)
const k = parseInt("abc", 10);                   // NaN

// Boolean to string:
const truthy = String(true);                     // "true"
const falsy = String(false);                     // "false"

// Array to string:
const arr = [1, 2, 3].join(", ");                // "1, 2, 3"
const arr2 = String([1, 2, 3]);                  // "1,2,3"

// Object to string:
const obj = JSON.stringify({ a: 1, b: 2 });      // '{"a":1,"b":2}'
const pretty = JSON.stringify(obj, null, 2);     // pretty-printed

The conventional discipline:

  • Use String(x) for explicit conversion.
  • Use template literals for formatting.
  • Use JSON.stringify for object serialisation.
  • Use parseInt with explicit radixparseInt("0x42") is parsed as hex.

Common patterns

String building

For substantial concatenation, array join is conventionally efficient:

// O(n²) (each += allocates a new string):
let s = "";
for (const item of items) {
    s += item.toString() + "\n";
}

// O(n):
const s = items.map(x => x.toString()).join("\n");

// Or with a more imperative style:
const parts: string[] = [];
for (const item of items) {
    parts.push(item.toString());
}
const s = parts.join("\n");

Template literal multi-line

const html = `
<!DOCTYPE html>
<html>
    <head>
        <title>${title}</title>
    </head>
    <body>
        <h1>${heading}</h1>
        <p>${content}</p>
    </body>
</html>
`;

const sql = `
    SELECT id, name, email
    FROM users
    WHERE active = $1
    ORDER BY created_at DESC
`;

String validation

function isEmpty(s: string | null | undefined): boolean {
    return s == null || s.trim() === "";
}

function isEmail(s: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
}

function isUUID(s: string): boolean {
    return /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(s);
}

Padding for tabular output

const items = [
    { name: "Alice", age: 30 },
    { name: "Bob", age: 25 },
];

for (const item of items) {
    console.log(`${item.name.padEnd(10)} ${String(item.age).padStart(3)}`);
}

Splitting and rejoining

function normalisePath(path: string): string {
    return path
        .split("/")
        .filter(p => p && p !== ".")
        .join("/");
}

function camelToSnake(name: string): string {
    return name.replace(/[A-Z]/g, (m, i) => (i === 0 ? "" : "_") + m.toLowerCase());
}

Discriminated string unions

type Theme = "light" | "dark" | "auto";

function applyTheme(t: Theme) {
    switch (t) {
        case "light": /* ... */ break;
        case "dark":  /* ... */ break;
        case "auto":  /* ... */ break;
    }
}

The pattern admits compile-time discrimination on string values.

URL parsing

const url = new URL("https://example.com/path?q=1&r=2");

console.log(url.host);                           // "example.com"
console.log(url.pathname);                       // "/path"
console.log(url.searchParams.get("q"));          // "1"

The URL class is conventional for URL manipulation.

Tagged template for SQL

function sql(strings: TemplateStringsArray, ...values: unknown[]): { text: string; values: unknown[] } {
    const text = strings.reduce((acc, str, i) => {
        return acc + str + (i < values.length ? `$${i + 1}` : "");
    }, "");
    return { text, values };
}

const query = sql`SELECT * FROM users WHERE id = ${userId} AND active = ${true}`;
// { text: "SELECT * FROM users WHERE id = $1 AND active = $2", values: [userId, true] }

The pattern admits SQL-injection-resistant query construction.

A note on Unicode

JavaScript’s string is UTF-16 — many “characters” are actually two UTF-16 code units. The conventional defences:

  • Use for ... of for code-point iteration.
  • Use [...s] or Array.from(s) for code-point arrays.
  • Use String.prototype.codePointAt in place of charCodeAt when handling user text.
  • Use the u flag on regex for code-point-aware patterns.
  • Use String.prototype.normalize before comparing user-input strings.

For grapheme cluster (“user-perceived character”) handling, the Intl.Segmenter (since ES2022) is conventional.

A note on the conventional discipline

The contemporary TypeScript strings advice:

  • Use single quotes for string literals; double for JSON.
  • Use template literals for interpolation and multi-line strings.
  • Use array .join() for substantial concatenation.
  • Use String(x) and template literals for conversion.
  • Use parseInt with radix for integer parsing.
  • Use for ... of for code-point-aware iteration.
  • Use literal string types for enum-like unions.
  • Use template literal types for compile-time string-type computation.
  • Use the URL class for URL parsing.

The combination — JavaScript’s string operations, template literals, the TypeScript-specific string-literal and template-literal types — is the substance of TypeScript’s text surface. The discipline produces clear, Unicode-aware string handling with substantial type-level computation.