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.
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.
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.
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.
const user = "Anna";
const age = 32;
const summary = `Benutzer: ${user}
Alter: ${age}
Status: ${age >= 18 ? "Erwachsen" : "Minderjährig"}`;
console.log(summary);Benutzer: Anna
Alter: 32
Status: ErwachsenInnerhalb 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.
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.
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.
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:
| Utility | Wirkung | Beispiel-Ergebnis |
|---|---|---|
Uppercase<S> | Alle Buchstaben groß | Uppercase<"abc"> → "ABC" |
Lowercase<S> | Alle Buchstaben klein | Lowercase<"ABC"> → "abc" |
Capitalize<S> | Erster Buchstabe groß | Capitalize<"hello"> → "Hello" |
Uncapitalize<S> | Erster Buchstabe klein | Uncapitalize<"Hello"> → "hello" |
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.
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 ResourceString-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.
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.
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.
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.
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.
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.
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.
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?
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?
type Email = ${string}@${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?
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?
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?
as const bei String-Arrays – wofür?
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#?
"\\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
- Everyday Types – TypeScript Handbook
- Template Literal Types – TypeScript Handbook
- Utility Types – TypeScript Handbook