Der Typ string ist neben number und boolean einer der zentralen Primitives in TypeScript und repräsentiert jeden Textwert. Auf den ersten Blick wirkt er trivial – ein paar Anführungszeichen, fertig. Doch im TypeScript-Ökosystem entfaltet string eine erstaunliche Tiefe: Über String Literal Types kannst Du Werte-Mengen exakt einschränken, und mit Template Literal Types (seit TypeScript 4.1) lassen sich daraus kompositorisch neue Typen ableiten. Diese Doku zeigt Dir Schritt für Schritt, wie Du den Typ korrekt einsetzt, häufige Stolperfallen vermeidest und seine fortgeschrittenen Features für typsichere APIs nutzt. Egal ob klassische String-Verarbeitung, präzise Domain-Modelle oder Compile-Zeit-Magie – am Ende kennst Du das ganze Spektrum.

string als Primitive

In TypeScript ist string (klein geschrieben) der primitive Typ für alle Textwerte. Er entspricht direkt dem JavaScript-Primitive und wird in der Praxis fast immer durch Typinferenz vom Compiler automatisch erkannt.

TypeScript Basis-Verwendung
let firstName: string = "Anna";
let city = "Berlin"; // Typ wird als string inferiert
const greeting: string = `Hallo, ${firstName}`;

Wichtig ist die strikte Unterscheidung zwischen string (Kleinschreibung, Primitive) und String (Großschreibung, Wrapper-Klasse mit Konstruktor-Funktion). Beide sind nicht zuweisungskompatibel und verhalten sich auch zur Laufzeit unterschiedlich.

TypeScript string vs. String
const primitive: string = "Hallo";       // Primitive
const wrapper: String = new String("Hallo"); // Objekt-Wrapper

// Folgende Zuweisung schlägt fehl
const x: string = wrapper;
// Type 'String' is not assignable to type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object.

console.log(typeof primitive); // "string"
console.log(typeof wrapper);   // "object"

Die Faustregel lautet: Immer den primitiven Typ string verwenden. Der Wrapper String taucht in der täglichen Arbeit nur als Konstruktor-Funktion zur Konvertierung auf (siehe Abschnitt 07), nicht als Typannotation.

String-Literale und Template Strings

TypeScript unterstützt – wie JavaScript – drei Notationsformen für String-Literale: Einfache Anführungszeichen, doppelte Anführungszeichen und Backticks (Template Literals). Funktional sind die ersten beiden identisch, Backticks bieten zusätzlich Interpolation und mehrzeilige Strings.

TypeScript Drei Notationsformen
const single: string = 'Hallo Welt';
const double: string = "Hallo Welt";
const template: string = `Hallo Welt`;

Echte Vorteile bringen Template Strings (Backticks) durch ihre Interpolation und mehrzeilige Schreibweise.

TypeScript Interpolation und Mehrzeiligkeit
const user = "Anna";
const age = 32;

const summary = `Benutzer: ${user}
Alter: ${age}
Status: ${age >= 18 ? "Erwachsen" : "Minderjährig"}`;

console.log(summary);
Output
Benutzer: Anna
Alter: 32
Status: Erwachsen

Innerhalb von ${ ... } darf ein beliebiger TypeScript-Ausdruck stehen – auch Funktionsaufrufe, Ternaries oder Property-Zugriffe. Der Compiler prüft jeden Ausdruck statisch.

String Literal Types

Ein String Literal Type beschränkt den Typ auf einen einzelnen konkreten String-Wert. Kombiniert mit Union-Typen entsteht daraus ein extrem präzises Werkzeug zur Modellierung endlicher Werte-Mengen.

TypeScript Beschränkte Werte-Menge
type Direction = "north" | "south" | "east" | "west";

function move(direction: Direction): void {
    console.log(`Bewegung nach ${direction}`);
}

move("north"); // OK
move("up");    // Fehler: Argument of type '"up"' is not assignable
               // to parameter of type 'Direction'.

Im Vergleich zu einem reinen string-Parameter hat das mehrere Vorteile:

  • Compile-Zeit-Sicherheit: Typos werden vor dem Build erkannt
  • Autovervollständigung: Editoren wie VS Code zeigen alle gültigen Werte
  • Selbstdokumentierender Code: Der Typ ersetzt eine Kommentar-Auflistung
  • Refactoring-Sicherheit: Beim Umbenennen eines Wertes weist der Compiler auf alle Verwendungsstellen hin

Häufig kombiniert man Literal Types mit as const, um aus einem Array oder Objekt automatisch eine Union abzuleiten.

TypeScript Union aus Array ableiten
const ROLES = ["admin", "editor", "viewer"] as const;
type Role = typeof ROLES[number];
// Role = "admin" | "editor" | "viewer"

function checkRole(role: Role): boolean {
    return ROLES.includes(role);
}

Template Literal Types

Seit TypeScript 4.1 gibt es Template Literal Types: Dieselbe Backtick-Syntax wie auf Wert-Ebene, aber auf Typ-Ebene. Damit lassen sich Typen kompositorisch aus anderen Typen aufbauen.

TypeScript Grundlegende Komposition
type Greeting = "hello" | "hi";
type Name = "Anna" | "Ben";

type FullGreeting = `${Greeting}, ${Name}!`;
// "hello, Anna!" | "hello, Ben!" | "hi, Anna!" | "hi, Ben!"

Bei mehreren Unions im Template entsteht das kartesische Produkt aller Kombinationen – elegant, aber bei vielen Faktoren schnell explosiv (siehe Insights-Sektion zur Compiler-Performance).

Intrinsische String-Manipulation

TypeScript bietet vier eingebaute Utility-Types, die ausschließlich auf Typ-Ebene arbeiten – ohne jegliche Laufzeit-Logik:

UtilityWirkungBeispiel-Ergebnis
Uppercase<S>Alle Buchstaben großUppercase<"abc">"ABC"
Lowercase<S>Alle Buchstaben kleinLowercase<"ABC">"abc"
Capitalize<S>Erster Buchstabe großCapitalize<"hello">"Hello"
Uncapitalize<S>Erster Buchstabe kleinUncapitalize<"Hello">"hello"
TypeScript Getter-Namen automatisch generieren
type Property = "name" | "age" | "email";
type Getter = `get${Capitalize<Property>}`;
// "getName" | "getAge" | "getEmail"

Praxisbeispiel: Typsichere Routen

Template Literal Types eignen sich hervorragend für Routen-Typing in einem Router oder API-Client.

TypeScript Routen mit dynamischen Segmenten
type Resource = "users" | "posts" | "comments";
type Action = "list" | "detail" | "create";

type ApiRoute =
    | `/api/${Resource}`
    | `/api/${Resource}/${number}`
    | `/api/${Resource}/${number}/edit`;

function call(route: ApiRoute): Promise<unknown> {
    return fetch(route).then(r => r.json());
}

call("/api/users");          // OK
call("/api/users/42");       // OK
call("/api/users/42/edit");  // OK
call("/api/foo");            // Fehler – "foo" kein gültiges Resource

String-Methoden und Type Safety

Alle Standard-Methoden des JavaScript-String-Prototyps sind in TypeScript voll typisiert. Der Compiler kennt Signaturen, Rückgabetypen und unterscheidet zwischen Methoden, die einen neuen String erzeugen, und solchen, die andere Strukturen wie Arrays oder Booleans liefern.

TypeScript Typische Methoden
const text: string = "  Hallo Welt  ";

const length: number = text.length;
const trimmed: string = text.trim();
const upper: string = trimmed.toUpperCase();
const parts: string[] = trimmed.split(" ");
const replaced: string = trimmed.replace("Welt", "TypeScript");
const slice: string = trimmed.slice(0, 5);
const includes: boolean = trimmed.includes("Hallo");

Da Strings in JavaScript immutable sind, geben fast alle Methoden einen neuen String zurück – das Original bleibt unverändert. Diese Eigenschaft ist nicht typsystemisch erzwungen, sondern Teil der Sprachsemantik. TypeScript hilft Dir dennoch beim Verketten von Operationen mit korrekten Zwischen-Typen.

TypeScript Method Chaining mit Typinferenz
function slugify(input: string): string {
    return input
        .trim()
        .toLowerCase()
        .replace(/\s+/g, "-")
        .replace(/[^a-z0-9-]/g, "");
}

string als Index Signature

In JavaScript sind Objekt-Schlüssel intern immer Strings (oder Symbols). TypeScript bildet das im Typsystem über Index Signatures ab.

TypeScript String-Index für Dictionaries
interface Translations {
    [key: string]: string;
}

const labels: Translations = {
    save: "Speichern",
    cancel: "Abbrechen",
    delete: "Löschen"
};

const value: string = labels["save"];

Auch der keyof-Operator liefert bei String-Properties eine String-Literal-Union zurück. Das macht ihn zu einem natürlichen Partner für string-basierte Typen.

TypeScript keyof liefert String-Literal-Union
interface User {
    id: string;
    name: string;
    email: string;
}

type UserKey = keyof User;
// "id" | "name" | "email"

function getField<K extends UserKey>(user: User, key: K): User[K] {
    return user[key];
}

Häufige Konvertierungen

In der Praxis musst Du regelmäßig Werte anderer Typen in Strings umwandeln. TypeScript bietet dafür mehrere Wege mit unterschiedlichem Verhalten und unterschiedlicher Typsicherheit.

TypeScript Vier Wege, einen Wert zu stringifizieren
const num = 42;
const flag = true;
const obj = { id: 1, name: "Anna" };

// 1) String()-Funktion (sicher für null/undefined)
const a: string = String(num);    // "42"
const b: string = String(null);   // "null"

// 2) toString()-Methode (wirft bei null/undefined)
const c: string = num.toString(); // "42"

// 3) Template Literal als Schnell-Cast
const d: string = `${num}`;       // "42"

// 4) JSON.stringify – für strukturierte Daten
const e: string = JSON.stringify(obj); // '{"id":1,"name":"Anna"}'

String(x) und Template Literals sind die robustesten Varianten, weil sie auch mit null und undefined umgehen können. toString() wirft hingegen eine TypeError-Ausnahme, sobald der Wert null oder undefined ist. JSON.stringify ist die einzige der vier Optionen, die strukturierte Objekte sauber serialisiert – aber Achtung bei zyklischen Referenzen oder BigInt-Werten.

Type Guard für Strings

Der einfachste und gebräuchlichste Type Guard in TypeScript ist typeof value === "string". Der Compiler erkennt das Muster und verengt den Typ innerhalb des Blocks automatisch auf string.

TypeScript Typ-Narrowing mit typeof
function describe(value: string | number | boolean): string {
    if (typeof value === "string") {
        // Hier ist value vom Typ string – .toUpperCase() verfügbar
        return `Text: ${value.toUpperCase()}`;
    }

    if (typeof value === "number") {
        return `Zahl: ${value.toFixed(2)}`;
    }

    return `Bool: ${value}`;
}

Für komplexere Fälle lässt sich ein benutzerdefinierter Type Guard schreiben, der ein Type Predicate zurückgibt.

TypeScript Benutzerdefinierter Type Guard
function isNonEmptyString(value: unknown): value is string {
    return typeof value === "string" && value.length > 0;
}

function processInput(input: unknown): void {
    if (isNonEmptyString(input)) {
        console.log(input.toUpperCase());
    } else {
        console.log("Kein gültiger Text");
    }
}

FAQ

Warum string klein und nie String groß?

string ist der Primitive-Typ, String ist die Wrapper-Klasse mit Konstruktor. Beide sind nicht zuweisungskompatibel, und new String("x") erzeugt ein Objekt, kein Primitive – mit allen unangenehmen Folgen bei Vergleichen und typeof. In Typannotationen ist daher immer die Kleinschreibung gewünscht.

Wann String Literal Types statt string?

Sobald eine endliche, bekannte Werte-Menge existiert. Statt status: string nutze status: "draft" | "published" | "archived". Du gewinnst Autovervollständigung, Typo-Schutz und Refactoring-Sicherheit. Faustregel: Wenn Du in der Doku eine Liste gültiger Werte schreiben müsstest, gehört die Liste in den Typ.

Wie typisiere ich Email, URL oder UUID?

Template Literal Types wie type Email = &#36;{string}@&#36;{string} sind nur eine grobe Form-Prüfung – die Bibliothek der gültigen Zeichen ist riesig, der Compiler kann hier kaum Garantien geben. Für echte Garantien nutzt man Branded Types: ein Primitive plus unsichtbares Tag, kombiniert mit einer Factory-Funktion, die zur Laufzeit validiert und das Brand setzt.

Was tut Capitalize – braucht das Runtime-Magic?

Nein. Capitalize, Uncapitalize, Uppercase und Lowercase sind intrinsische Compile-Zeit-Operationen. Sie wirken ausschließlich auf String-Literal-Typen und erzeugen keinen einzigen Byte JavaScript-Output. Zur Laufzeit musst Du weiterhin toUpperCase() & Co. aufrufen – das Typsystem und die Laufzeit sind hier völlig getrennt.

Verengen startsWith/endsWith den Typ?

Seit TypeScript 4.3 ja – aber nur in Kombination mit Literal-Typen. Bei einem Wert vom Typ string wird kein Narrowing erzeugt, weil das Ergebnis nichts über das konkrete Pattern aussagt. Bei einer Union wie "/api/users" | "/api/posts" hingegen verengt route.startsWith("/api/users") die Variable korrekt.

Compiler wird langsam – liegt es an Template Literal Types?

Sehr wahrscheinlich. Template Literal Types erzeugen das kartesische Produkt aller Unions. Drei Unions mit je 20 Werten ergeben bereits 8000 Typen. Halte Routen-Typen, Event-Typen und ähnliches schlank, oder reduziere die Komplexität durch konkrete Sub-Typen. Im Zweifel hilft der TypeScript Compiler-Trace.

as const bei String-Arrays – wofür?

Ohne as const inferiert TypeScript ein string[] – jede Information über die konkreten Werte ist verloren. Mit as const wird das Array readonly und behält seine Literal-Typen. Daraus lässt sich per typeof ARRAY[number] eine String-Literal-Union ableiten – ideal für Single Source of Truth zwischen Laufzeit-Daten und Typ.

Hat TypeScript raw-Strings wie Python oder C#?

Nein, native raw-String-Literale gibt es nicht. Backslashes müssen verdoppelt werden ("\\n" statt r"\n"). Als Workaround gibt es String.raw: String.raw\n\t liefert die wortwörtlichen Zeichen \n\t statt eines Zeilenumbruchs gefolgt von einem Tab – nützlich für Regex-Patterns oder Windows-Pfade.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Primitive Typen

Zur Übersicht