Declaration Merging ist eine der eigentümlichsten, aber zugleich praxiswichtigsten Eigenschaften von TypeScript: sobald du zwei oder mehr gleichnamige Deklarationen im selben Scope absetzt, verschmilzt der Compiler sie automatisch zu einer einzigen, kombinierten Definition. Bei interface ist das die offizielle, dokumentierte Mechanik — und sie ist der Grund, warum interface und type trotz oberflächlicher Austauschbarkeit eben nicht dasselbe sind. Wer Express.Request um ein user-Feld erweitert, wer Window ein Analytics-Global hinzufügt oder wer Jest neue Matcher unterschiebt, nutzt unbewusst Declaration Merging — und stolpert garantiert über die Regeln, sobald die ersten Property-Konflikte auftauchen. Dieser Artikel zeigt dir Schritt für Schritt, was wie verschmolzen wird, wo die harten Grenzen liegen und wie die zwei wichtigsten Praxis-Fälle — Module Augmentation und globale Augmentation — wirklich funktionieren.

Was Declaration Merging ist

Declaration Merging beschreibt die Compiler-Regel, dass zwei Deklarationen mit demselben Namen im selben Scope nicht als Konflikt behandelt werden, sondern zu einer einzigen Deklaration verschmelzen. Die Mechanik betrifft eine genau definierte Liste von Konstrukten: interface, namespace, enum und Kombinationen daraus mit class, function oder enum. Was sie nicht betrifft: type-Aliase, const/let und einfache Klassen — diese werfen bei Doppeldeklaration einen Compile-Fehler.

Für die Interface-Welt ist die Regel verblüffend einfach: zwei Blöcke mit dem gleichen Namen werden vom Compiler so behandelt, als hättest du einen einzigen Block geschrieben, der alle Member beider Blöcke enthält. Das ist der gesamte Kern — und gleichzeitig die Grundlage für Module Augmentation, globale Augmentation und das nachträgliche Erweitern fremder Library-Typen.

ts kern-idee.ts
// Zwei Deklarationen — gleicher Name, gleicher Scope.
interface User {
    id: number;
}

interface User {
    name: string; // wird zur ersten Deklaration hinzu-gemerged
}

// Effektiver Typ aus Sicht des Compilers:
//   interface User { id: number; name: string }
const u: User = { id: 1, name: "Anna" }; // beide Felder Pflicht

Das wirkt im ersten Moment wie ein Trick, ist aber eine bewusst entworfene Eigenschaft. Sie existiert, weil JavaScript schon vor TypeScript reichlich Code hatte, der Prototypen, globale Objekte und Modul-Exports nachträglich erweiterte. TypeScript brauchte eine Möglichkeit, diese realen Patterns typsicher abzubilden — Declaration Merging ist die Antwort.

Erstes Beispiel: zwei lokale Deklarationen

Schauen wir das Pattern an einem zusammenhängenden Beispiel an. Wir deklarieren zweimal interface Box mit unterschiedlichen Members und sehen, wie der Compiler das Ergebnis behandelt.

ts box-merge.ts
// Deklaration 1 — beschreibt die Maße.
interface Box {
    height: number;
    width: number;
}

// Deklaration 2 — fuegt ein weiteres Feld hinzu.
interface Box {
    scale: number;
}

// Effektiv vom Compiler so gesehen:
//   interface Box {
//       height: number;
//       width: number;
//       scale: number;
//   }

// Beim Anlegen sind ALLE Felder Pflicht — der Compiler kennt nur das Merge-Ergebnis.
const box: Box = { height: 5, width: 6, scale: 10 };

// Fehler: Property 'scale' is missing in type '{ height: number; width: number; }'.
// const broken: Box = { height: 5, width: 6 };

Wichtig ist die mentale Repräsentation: aus Sicht jedes Codes, der Box benutzt, existiert eine Box-Definition mit drei Feldern. Die Tatsache, dass diese Definition aus zwei Quellen zusammengesetzt wurde, ist nur für den Leser des Quelltextes sichtbar — nicht für irgendeinen Konsumenten weiter unten. Genau das macht das Pattern für Library-Augmentation so brauchbar: dein eigener Code kann fremde Typen erweitern, ohne die Original-Library zu patchen.

Member-Merging-Regeln

Verschmelzen klingt nach „alles geht zusammen", ist aber tatsächlich an klare Regeln gebunden, die sich je nach Member-Typ unterscheiden. Es gibt im Wesentlichen drei Fälle: gewöhnliche Properties, Methoden-Signaturen und Konflikte.

KonstellationVerhalten
Property in nur einer Deklarationwird in den Merge-Output übernommen
Property in beiden, identischer Typerlaubt, einmal im Merge-Output
Property in beiden, unterschiedlicher TypCompile-Fehler — kein silent overwrite
Methode in beiden (Function-Member)als Overload-Signaturen verschmolzen
Spezialisierte String-Literal-Overloadswandern automatisch nach oben in der Resolution-Reihenfolge

Das wichtigste Detail: Property-Konflikte sind harte Fehler, kein lautloses Überschreiben. Wenn du in Deklaration 1 id: number und in Deklaration 2 id: string schreibst, weigert sich der Compiler — und das ist gut so, weil ein silent merge an dieser Stelle ein massives Sicherheitsleck wäre.

ts konflikt.ts
interface Account {
    id: number;       // Erste Deklaration sagt: number.
    balance: number;
}

interface Account {
    id: string;       // !! Zweite Deklaration sagt: string — Typ-Konflikt.
    owner: string;
}

// Fehler: Subsequent property declarations must have the same type.
//         Property 'id' must be of type 'number', but here has type 'string'.

Bei Methoden funktioniert das Merging anders: hier werden die Signaturen zu einem Overload-Set zusammengeführt, das heisst alle Signaturen bleiben erhalten, und der Compiler wählt zur Aufruf-Zeit die passende. Die Reihenfolge der Overloads ist relevant — der Compiler probiert sie von oben nach unten durch und nimmt die erste passende.

ts overload-merge.ts
interface Cloner {
    clone(animal: Dog): Dog;
}

interface Cloner {
    clone(animal: Cat): Cat;
}

interface Cloner {
    clone(animal: Animal): Animal;
}

// Effektives Overload-Set (Reihenfolge wie aufgefuehrt):
//   clone(animal: Dog): Dog;
//   clone(animal: Cat): Cat;
//   clone(animal: Animal): Animal;

// Sonderregel: Signaturen mit einzelnen String-Literal-Parametern wandern
// AUTOMATISCH an den Anfang — typisches Beispiel ist document.createElement,
// wo "canvas"/"div"/... vor der generischen string-Signatur ranken.

interface Animal { species: string }
interface Dog extends Animal {}
interface Cat extends Animal {}

Praktisch heisst das: spezialisierte Signaturen schreibst du zuerst, generische Fallbacks zuletzt. Bei einfachen Property-Mergen ist Reihenfolge egal, bei Methoden-Overloads ist sie sehr wohl bedeutend.

Namespace-Merging

Neben Interfaces ist namespace das zweite Konstrukt, das mergebar ist — und zwar in mehrere Richtungen gleichzeitig: zwei namespace-Blöcke mergen miteinander, ein namespace mergt mit einer gleichnamigen class, mit einem function oder mit einem enum. Das ist das klassische Pattern für „Klasse mit Static-Members" aus der Zeit vor ES6-Klassen, und es überlebt bis heute in Library-Code.

ts namespace-merge.ts
// Eine Funktion plus ein gleichnamiger Namespace — der Namespace
// wird zur "Static-Properties-Tasche" der Funktion.
function buildLabel(name: string): string {
    return buildLabel.prefix + name + buildLabel.suffix;
}

namespace buildLabel {
    export let prefix = "Hallo, ";
    export let suffix = "!";
}

console.log(buildLabel("Anna")); // "Hallo, Anna!"

// Aufgeloest als wuerdest du buildLabel.prefix / .suffix auf der Funktion ablegen.
// Funktioniert auch mit Klassen: Inner-Class-Pattern.

class Album {
    label!: Album.AlbumLabel;
}
namespace Album {
    export class AlbumLabel {}
}

Eine wichtige Stolperfalle: nicht-exportierte Member eines Namespace-Blocks sind im Merge-Partner nicht sichtbar. Wenn du in Block 1 eine private Variable hast und in Block 2 darauf zugreifen willst, scheitert das. Das fühlt sich inkonsistent an, ist aber eine bewusste Kapselung pro Deklarations-Block.

ts namespace-private.ts
namespace Animal {
    let haveMuscles = true; // nicht exportiert — block-lokal
    export function animalsHaveMuscles() {
        return haveMuscles; // OK
    }
}

namespace Animal {
    export function doAnimalsHaveMuscles() {
        // Fehler: Cannot find name 'haveMuscles'.
        // return haveMuscles;
        return Animal.animalsHaveMuscles(); // Workaround über exportierte Brücke
    }
}

Für reines Modul-/Library-Design verwendet man heute fast nur noch ES-Module statt Namespaces. Dass das Namespace-Merging trotzdem wichtig bleibt, liegt am Zusammenspiel mit Module Augmentation und an Library-Typen, die historisch auf Namespace-Strukturen aufgebaut sind (z. B. ältere DefinitelyTyped-Pakete).

Module Augmentation — Library-Typen erweitern

Hier wird Declaration Merging zum Produktiv-Werkzeug. Module Augmentation ist die Technik, mit der du ein importiertes ES-Modul nachträglich um zusätzliche Member erweiterst — typischerweise um Properties an einem Interface, das die Library exportiert. Du arbeitest dabei nicht im Original-Modul, sondern in deinem eigenen Code; der Compiler verschmilzt die Erweiterungen trotzdem auf der Typ-Ebene mit den Library-Typen.

Die Syntax ist ein declare module "modul-name"-Block, in dem du die zu erweiternden Interfaces ganz normal nochmal deklarierst. Voraussetzung: das aktuelle File muss selbst ein Modul sein (mindestens ein import oder export), sonst wird der Block als globaler Namespace interpretiert.

ts observable.ts
// Datei 1: observable.ts — die "Library".
export class Observable<T> {
    // ...
}
ts map-plugin.ts
// Datei 2: map-plugin.ts — Plugin, das Observable erweitert.
import { Observable } from "./observable";

// Augmentation des Moduls: gleichnamiges Interface hinzufuegen.
declare module "./observable" {
    interface Observable<T> {
        map<U>(f: (x: T) => U): Observable<U>;
    }
}

// Runtime-Teil: Methode wirklich am Prototyp anhaengen.
// Typen alleine reichen nicht — JS muss die Funktion auch kennen.
Observable.prototype.map = function (f) {
    // ... reale Implementierung ...
    return this as any;
};
ts consumer.ts
// Datei 3: consumer.ts — benutzt Library + Plugin.
import { Observable } from "./observable";
import "./map-plugin"; // wichtig: laedt das Augmentation-Modul

declare const o: Observable<number>;

// map() ist hier verfügbar — Compiler sieht das Augmentation-Interface.
const mapped = o.map(x => x.toFixed(2));

Zwei Beschränkungen sind wichtig zu wissen. Erstens: Module Augmentation erweitert nur vorhandene Exports — neue Top-Level-Exports lassen sich auf diesem Weg nicht hinzufügen. Zweitens: Default-Exports können nicht augmentiert werden, nur named exports. Wer eine Library hat, die ihr Hauptklassen-Objekt als Default exportiert, muss zur Re-Export-Bridge greifen oder die Library um einen named Re-Export bitten.

Globale Augmentation — Window/Process erweitern

Der prominenteste Anwendungsfall im Frontend: das Window-Objekt soll um eine projektspezifische Property erweitert werden — sei es ein Analytics-Tracker, ein Feature-Flag-Container oder ein Debug-Hook. Im Backend zieht das Pattern mit NodeJS.ProcessEnv parallel.

Die Mechanik ist eine Variante der Module Augmentation: statt declare module "..." schreibst du declare global. Wichtige Bedingung: die Datei muss selbst ein Modul sein. Ist sie das nicht — weil sie kein import und kein export enthält —, interpretiert der Compiler den Block bereits als globale Deklaration, und declare global wird redundant oder verwirrend. Der konventionelle Trick ist eine leere export {}-Zeile am Ende einer ansonsten reinen .d.ts-Datei.

ts global.d.ts
// src/types/global.d.ts — globale Augmentation für Window und Process.
export {}; // wandelt die Datei in ein Modul um — wichtig für `declare global`

declare global {
    interface Window {
        // Tracking-Library hängt sich hier ein.
        analytics?: {
            track(event: string, props?: Record<string, unknown>): void;
            identify(userId: string): void;
        };
        // Feature-Flag-Container.
        __featureFlags?: Readonly<Record<string, boolean>>;
    }

    namespace NodeJS {
        interface ProcessEnv {
            // Pflicht-Variablen aus deinem .env.
            DATABASE_URL: string;
            JWT_SECRET: string;
            NODE_ENV: "development" | "test" | "production";
        }
    }
}

Damit der TypeScript-Compiler diese Datei auch wirklich sieht, muss sie über die tsconfig.json eingebunden werden — entweder über include: ["src/**/*.d.ts"] (was per Default ohnehin matcht) oder explizit via files. Wenn du das vergisst, sehen einzelne Dateien die Augmentation nicht, und du wunderst dich, warum window.analytics mal als typed, mal als any erscheint.

ts consumer.ts
// Beliebige Datei im Projekt — Augmentation greift global.
window.analytics?.track("page_view", { url: location.href });

// Im Node-Code: process.env trifft die genauen Typen aus dem Augmentation-Block.
const url: string = process.env.DATABASE_URL; // garantiert string, nicht string|undefined

Praxis: Express-Request-Erweiterung

Das Paradebeispiel für Module Augmentation in der Backend-Welt ist das Anreichern von express.Request mit einem user-Feld, das deine Auth-Middleware setzt. Ohne Augmentation müsstest du in jedem Handler einen Cast schreiben ((req as any).user) — mit Augmentation typisiert TypeScript den Zugriff sauber durch.

ts types/express.d.ts
// src/types/express.d.ts
// Augmentation des @types/express-Pakets — gleicher Modulname.

import "express"; // sorgt dafuer, dass die Modul-Typen geladen sind

// Das eigene User-Objekt, wie es die Auth-Middleware produziert.
interface AuthUser {
    id: number;
    email: string;
    roles: readonly string[];
}

declare module "express-serve-static-core" {
    // Express deklariert Request in diesem Sub-Modul — nicht in "express" direkt.
    interface Request {
        user?: AuthUser; // optional, weil bei öffentlichen Routen ggf. nicht gesetzt
    }
}

Der Trick mit "express-serve-static-core" statt "express" ist nicht offensichtlich, aber notwendig: das Express-Hauptpaket re-exportiert die Request-Interfaces aus dem Core-Paket, und genau dort liegt die mergebare Definition. Wer dieses Detail nicht kennt, kämpft stundenlang mit Augmentations, die scheinbar wirkungslos bleiben.

ts auth-middleware.ts
import type { Request, Response, NextFunction } from "express";

// Middleware setzt req.user — der Compiler kennt das Feld dank Augmentation.
export function requireAuth(req: Request, res: Response, next: NextFunction) {
    const token = req.headers.authorization?.replace(/^Bearer /, "");
    if (!token) {
        res.status(401).json({ error: "Token fehlt" });
        return;
    }
    // ... Token verifizieren ...
    req.user = { id: 42, email: "anna@example.com", roles: ["admin"] };
    next();
}

// Handler — req.user ist getypt, kein Cast noetig.
export function meHandler(req: Request, res: Response) {
    if (!req.user) {
        res.status(401).end();
        return;
    }
    res.json({ email: req.user.email, roles: req.user.roles });
}

Achtung beim Deployen in Monorepos: jedes Paket, das die augmentierten Typen benutzen will, muss die .d.ts-Datei über seine eigene tsconfig.json einbinden — Augmentation ist datei-lokal, nicht magisch projekt-übergreifend.

Praxis: Window-Properties

Ein zweiter sehr häufiger Fall im Frontend: eine Tracking-Library injiziert ein globales dataLayer, ein Cookie-Consent-Skript hängt sich an window, oder du baust selbst einen Debug-Endpunkt für dein Dev-Tooling. Ohne Augmentation jeder Zugriff ein (window as any).dataLayer — mit Augmentation sauberer Code.

ts analytics.d.ts
// src/types/analytics.d.ts
export {};

declare global {
    interface Window {
        // Google-Tag-Manager-Style: ein Array, das Events sammelt, bis GTM laedt.
        dataLayer: Record<string, unknown>[];

        // Eigener Debug-Hook, nur in Development.
        __myAppDebug?: {
            dumpState(): void;
            setFlag(name: string, value: boolean): void;
        };
    }
}
ts tracking.ts
// Initialisierung — dataLayer ist garantiert vorhanden (nicht optional deklariert).
window.dataLayer = window.dataLayer ?? [];
window.dataLayer.push({ event: "app_ready", ts: Date.now() });

// Cleanup-Hinweis: wenn du Debug-Hooks anlegst, denke an Teardown.
if (import.meta.env?.DEV) {
    window.__myAppDebug = {
        dumpState: () => console.log("..."),
        setFlag: (n, v) => console.log(`flag ${n}=${v}`),
    };
}
// Beim Hot-Reload oder Logout: window.__myAppDebug = undefined;

Wichtig ist die bewusste Entscheidung optional oder nicht. dataLayer als Pflichtfeld zwingt dich dazu, es im Bootstrap sicher zu initialisieren — was du ohnehin tun solltest, wenn dein Code ihn überall benutzt. Debug-Hooks dagegen sind klar optional, weil sie in Production gar nicht existieren sollen.

type vs. interface bei Augmentation

Wir nähern uns dem härtesten Unterschied zwischen type und interface. In den meisten Alltagsfällen sind die beiden austauschbar — Objektformen, Function-Typen, Property-Listen funktionieren mit beiden gleich. Aber: Declaration Merging funktioniert ausschliesslich mit interface. Ein type-Alias kann nicht ein zweites Mal deklariert werden, schon gar nicht zur Erweiterung.

ts type-vs-interface.ts
// interface — mergebar, funktioniert.
interface OK {
    a: number;
}
interface OK {
    b: number;
}
const ok: OK = { a: 1, b: 2 }; // beide Felder erkannt.

// type — NICHT mergebar, harter Fehler.
type Broken = { a: number };
// type Broken = { b: number };
// Fehler: Duplicate identifier 'Broken'.

Das ist der Grund, warum jedes ernstzunehmende TypeScript-Library-Paket — Express, React, Jest, Vite, RxJS — seine öffentlichen Erweiterungspunkte als interface deklariert. Würden sie type benutzen, könnte niemand die Typen von aussen ergänzen. Wer eine eigene Library baut und seinen Nutzern Augmentation-Punkte ermöglichen will, muss an diesen Stellen interface verwenden.

Kriteriuminterfacetype
Declaration Mergingja, mehrfach deklarierbarnein, doppelt = Fehler
Module Augmentation möglichjanein
Globale Augmentation möglichjanein
Union-Typen direkt definierenneinja (A | B)
Mapped/Conditional Typesneinja
Tuple-Typnur indirektja, idiomatisch

Die praktische Faustregel daraus: öffentliche Objekt-Shapes als interface, alles andere als type. So bleibt der Augmentation-Pfad offen, ohne dass du auf die expressiveren Type-Konstrukte verzichtest, wo sie sinnvoll sind.

Stolperfallen

Declaration Merging ist mächtig, aber genau diese Mächtigkeit produziert Fehlerklassen, die schwer zu debuggen sind. Drei der häufigsten Fallen siehst du immer wieder.

Stolperfalle 1: versehentliches Merging zweier Interfaces, die unabhängig sein sollten. In grossen Codebases mit vielen Modulen kann es passieren, dass zwei Teams unabhängig voneinander ein Interface namens Config definieren — und sobald beide Dateien im selben Compilation-Scope landen, verschmelzen sie. Das Ergebnis: ein riesiger, inkonsistenter Mischtyp, der Felder aus beiden Welten enthält und in keiner mehr richtig funktioniert. Die Lösung: domänenspezifische Prefixes (AppConfig, DbConfig) und konsequent in Module statt globalem Namespace arbeiten.

ts versehen.ts
// file-a.ts (global, kein export)
interface Config { dbUrl: string }

// file-b.ts (global, kein export)
interface Config { theme: "light" | "dark" }

// Ergebnis: Config hat BEIDE Felder — mit hoher Wahrscheinlichkeit ungewollt.
// Werte ohne theme oder ohne dbUrl werden plötzlich vom Compiler abgelehnt.

Stolperfalle 2: Augmentation greift nicht, weil die .d.ts-Datei nicht eingebunden ist. Wer eine globale Augmentation in src/types/global.d.ts ablegt und vergisst, sie über include in der tsconfig.json zu listen, wundert sich, warum window.analytics immer noch als any erscheint. TypeScript ist hier still — es meckert nicht, dass deine Datei ignoriert wird, sondern verhält sich nur so, als gäbe es sie nicht.

Stolperfalle 3: declare global in einer Nicht-Modul-Datei. Wenn deine .d.ts weder import noch export enthält, ist sie automatisch globaler Scope — und declare global wird zur Tautologie. Im besten Fall warnt der Compiler, im schlechtesten Fall passieren subtil falsche Merges. Faustregel: jede .d.ts-Datei mit declare global bekommt am Ende ein export {};.

Stolperfalle 4: Augmentation eines Default-Exports. Du kannst nur named exports augmentieren. Wenn die Library ihre Hauptklasse als export default class Foo herausgibt, hast du keine Möglichkeit, sie direkt zu erweitern. Workaround: ein eigenes Modul, das die Klasse importiert, als named Export reexportiert und dort augmentiert wird — wobei das oft mehr Aufwand ist als ein sauberes Wrapper-Pattern.

Stolperfalle 5: Mehrere .d.ts mit unterschiedlichen Augmentations am selben Target. Wenn zwei Plugins beide Window um ein Feld flags erweitern, kommt es bei unterschiedlichen Typen zu Konflikten — bei gleichen Typen ist es OK, aber bei abweichenden Strukturen scheitert der Compiler hart. Das ist die Library-Seite des Problems aus Stolperfalle 1 und passiert in node_modules-Setups erstaunlich oft, wenn zwei DefinitelyTyped-Pakete sich überlappen.

Besonderheiten

Declaration Merging funktioniert NUR mit interface, nicht mit type.

Das ist der entscheidende strukturelle Unterschied zwischen den beiden. type-Aliase werfen bei doppelter Deklaration einen Duplicate identifier-Fehler, interface-Bloecke verschmelzen automatisch — und genau deshalb verwenden alle ernstzunehmenden Library-Pakete interface für ihre öffentlichen Erweiterungspunkte.

Property-Konflikte beim Merging werden zu Compile-Fehlern, nicht silent.

Wenn zwei interface-Bloecke dieselbe Property mit unterschiedlichen Typen deklarieren, schreit der Compiler mit Subsequent property declarations must have the same type. Es gibt kein unbemerktes Überschreiben — das ist die Sicherheits-Garantie hinter dem ganzen Pattern.

Methoden-Merging erzeugt Overloads, und die Reihenfolge zaehlt.

Bei Funktions-Members werden die Signaturen zu einem Overload-Set zusammengefügt — von oben nach unten durchprobiert. Spezialisierte Signaturen mit String-Literal-Parametern wandern automatisch nach oben (Sonderregel für Dinge wie document.createElement("canvas")).

Module Augmentation braucht den declare-module-Block.

Innerhalb eines Modul-Files schreibst du declare module "modul-name" und packst gleichnamige Interface-Definitionen rein. Voraussetzung: die Datei muss selbst ein Modul sein (mindestens ein import oder export) — sonst wird der Block als globaler Namespace fehl-interpretiert.

Globale Augmentation braucht declare global UND eine Modul-Datei.

Der Trick mit export {} am Ende einer .d.ts-Datei ist kein Stilmittel, sondern eine harte Voraussetzung: ohne diese Zeile ist die Datei kein Modul, und declare global wird im besten Fall redundant, im schlechtesten verwirrend.

tsconfig.json muss die .d.ts mit den Augmentations referenzieren.

Augmentation-Dateien, die über include oder files nicht erfasst sind, werden vom Compiler stillschweigend ignoriert. Pruefe im Zweifel mit tsc --listFiles, ob deine globalen Typen wirklich Teil des Compilation-Scopes sind.

Express-Request, Jest-Matchers, Vite-ImportMeta sind klassische Augmentation-Targets.

Diese drei Bibliotheken sind explizit für Augmentation entworfen — Express stellt express-serve-static-core bereit, Jest jest.Matchers, Vite ImportMetaEnv. Wer mit ihnen arbeitet, MUSS Declaration Merging verstehen, sonst landet er in Cast-Höllen.

Augmentation funktioniert auch für Klassen und Functions über namespace.

Mit dem Namespace-Merging-Trick kannst du Klassen statische Members untergeschoben oder Funktionen Property-Taschen anhaengen. Das ist die Mechanik hinter Patterns wie buildLabel.prefix oder dem "Inner-Class"-Pattern (Album.AlbumLabel).

Bei mehreren .d.ts-Dateien mit gleichem Augmentation-Target werden alle vereint.

Der Compiler unterscheidet nicht, in welcher Datei eine Augmentation steht — alle declare module "..."-Bloecke mit gleichem Modulnamen verschmelzen. Das ist hilfreich, wenn mehrere Plugins denselben Erweiterungspunkt nutzen, aber gefaehrlich, wenn ihre Felder mit unterschiedlichen Typen kollidieren.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Interfaces

Zur Übersicht