API Extractor für TypeScript-Bibliotheken einsetzen
AI generated
<T>
type
TypeScript · API Extractor · CI/CD · Dokumentation
API Extractor für TypeScript-Bibliotheken einsetzen
von vielen .d.ts-Dateien zu einer stabilen öffentlichen API

Eine TypeScript-Bibliothek mit Dutzenden internen Modulen erzeugt beim Standard-Build genauso viele einzelne Typdefinitionsdateien, ohne dass irgendwo sichtbar wird, was davon eigentlich zur öffentlichen API gehört. API Extractor löst genau dieses Problem: ein einziger Rollup, ein maschinenlesbarer API-Report als Contract-Test und Release Tags, die genau festlegen, was Konsumenten verwenden dürfen.

17 Min. Lesezeit api-extractor.json · API-Report · Release Tags · API Documenter TypeScript 5.x · CI/CD

1. Das Problem: viele einzelne .d.ts-Dateien statt einer stabilen API

Der TypeScript-Compiler erzeugt für jede Quelldatei eine eigene .d.ts-Datei. Bei einer kleinen TypeScript-Bibliothek mit einer Handvoll Dateien ist das kein Problem, bei einer gewachsenen Bibliothek mit fünfzig oder hundert internen Modulen entsteht dagegen ein unübersichtliches Geflecht aus Typdefinitionsdateien, die sich gegenseitig importieren. Für Konsumenten und für das eigene Team wird dabei nicht klar ersichtlich, welche dieser vielen Dateien tatsächlich zur öffentlichen API gehören und welche reine interne Implementierungsdetails sind.

Ohne ein Werkzeug wie API Extractor bleibt die einzige Absicherung gegen versehentlich exportierte interne Details ein manuelles Code-Review, das bei jeder Änderung erneut vollständig durchgeführt werden müsste. Das skaliert bei einer wachsenden TypeScript-Bibliothek nicht, und genau hier setzt API Extractor an: es macht die tatsächliche öffentliche API sichtbar, überprüfbar und maschinell durchsetzbar, statt sie stillschweigend der Disziplin einzelner Entwickler zu überlassen.

2. Was API Extractor macht: Rollup, API-Report, Doc Model

API Extractor, ein Open-Source-Tool aus dem Rush-Stack-Projekt von Microsoft, erfüllt drei zusammenhängende Aufgaben für eine TypeScript-Bibliothek. Erstens erzeugt es aus den vielen einzelnen, vom Compiler generierten .d.ts-Dateien eine einzige zusammengefasste Rollup-Deklarationsdatei, die ausschließlich die tatsächlich exportierten Symbole enthält, ohne interne Implementierungsdetails preiszugeben. Zweitens erzeugt es den bereits erwähnten API-Report, eine kompakte, versionskontrollierte Textdatei mit der vollständigen öffentlichen Signatur.

Drittens erzeugt API Extractor optional ein sogenanntes API Doc Model, eine maschinenlesbare JSON-Repräsentation der gesamten öffentlichen API inklusive aller JSDoc-Kommentare. Dieses Doc Model ist die Grundlage für automatisch generierte Dokumentation mit dem Schwesterprojekt API Documenter, das aus dem Doc Model lesbare Markdown-Seiten für jede exportierte Klasse, Funktion und jeden exportierten Typ erzeugt.

3. Installation und Konfiguration von api-extractor.json

Die Installation erfolgt über npm install --save-dev @microsoft/api-extractor, gefolgt von einer Konfigurationsdatei api-extractor.json im Projektwurzelverzeichnis. Die wichtigste Angabe ist mainEntryPointFilePath, der Pfad zur generierten .d.ts-Datei des Haupteinstiegspunkts, typischerweise dist/index.d.ts. API Extractor liest diese Datei und folgt allen darin referenzierten Importen, um die vollständige öffentliche API zu ermitteln.

Zusätzlich definiert die Konfiguration, wohin der Rollup und der API-Report geschrieben werden, sowie ob und wie das Doc Model erzeugt wird. Eine typische Konfiguration für eine TypeScript-Bibliothek aktiviert sowohl docModel als auch apiReport, während tsdocMetadata meist deaktiviert bleibt, sofern keine separate TSDoc-Tooling-Integration geplant ist.


{
  "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
  "mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts",
  "apiReport": {
    "enabled": true,
    "reportFolder": "<projectFolder>/etc/",
    "reportTempFolder": "<projectFolder>/temp/"
  },
  "docModel": {
    "enabled": true,
    "apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json"
  },
  "dtsRollup": {
    "enabled": true,
    "untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts"
  },
  "messages": {
    "extractorMessageReporting": {
      "ae-missing-release-tag": { "logLevel": "warning" }
    }
  }
}

4. Der API-Report als Contract-Test in CI

Der API-Report ist die zentrale Innovation von API Extractor für Backwards Compatibility: eine .api.md-Datei mit der vollständigen öffentlichen Signatur der TypeScript-Bibliothek in einem stabilen, diff-freundlichen Textformat. Diese Datei wird ins Repository eingecheckt wie jede andere Quelldatei. Bei jedem Build vergleicht API Extractor den frisch generierten Report mit der eingecheckten Version und meldet eine Abweichung als Fehler, wenn beide nicht übereinstimmen.

Dieser Mechanismus verwandelt den API-Report faktisch in einen Contract-Test: jede Änderung an der öffentlichen API muss explizit im Pull Request sichtbar werden, weil sich die .api.md-Datei mit ändert und im Diff des Reviewers auftaucht. Ein Reviewer sieht damit auf einen Blick, ob ein vermeintlich internes Refactoring versehentlich die öffentliche Signatur berührt hat, ohne die gesamte Codebasis manuell durchsuchen zu müssen.

# etc/query-builder.api.md — checked into version control
## API Report File for "@mironsoft/query-builder"

```ts
export class QueryBuilder {
  constructor(options: QueryBuilderOptions);
  // (undocumented)
  limit(count: number): this;
  where(conditions: WhereClause[]): this;
}

export interface QueryBuilderOptions {
  dialect: "mysql" | "postgres";
  timeoutMs?: number;
}
```

5. Warnungen: fehlende Release-Tags und ungewollte Exports

API Extractor erzeugt eine Reihe standardisierter Warnungen, die typische Fehler beim API-Design einer TypeScript-Bibliothek aufdecken. Die häufigste ist ae-missing-release-tag, die anzeigt, dass ein exportiertes Symbol keinen Release Tag wie @public oder @internal trägt. Ohne dieses Tag kann API Extractor nicht automatisch entscheiden, ob das Symbol tatsächlich für Konsumenten gedacht ist.

Eine zweite wichtige Warnung ist ae-forgotten-export, die auftritt, wenn ein öffentlich exportiertes Symbol intern einen Typ verwendet, der selbst nicht exportiert wird. Das Ergebnis für Konsumenten wäre ein Typ, der zwar in der API sichtbar auftaucht, aber nicht direkt importierbar ist, ein subtiler und für Konsumenten frustrierender Zustand. Diese Warnung deckt genau solche halbfertigen Export-Situationen zuverlässig auf, bevor Konsumenten sie melden.

6. Release Tags: public, beta, alpha, internal

Release Tags sind spezielle JSDoc-Tags, mit denen jedes exportierte Symbol einer TypeScript-Bibliothek explizit als @public, @beta, @alpha oder @internal markiert wird. @public kennzeichnet stabile API, für die volle Backwards-Compatibility-Garantien gelten. @beta markiert experimentelle, aber bereits nutzbare API, bei der sich die Signatur noch ändern kann, ohne dass dies als Major-Release-würdiger Bruch zählt. @alpha ist für frühe, instabile Vorabversionen gedacht, @internal für Symbole, die aus technischen Gründen exportiert werden müssen, aber niemals von Konsumenten verwendet werden sollen.

Der praktische Nutzen zeigt sich beim Rollup: API Extractor kann getrennte .d.ts-Dateien für unterschiedliche Veröffentlichungsstufen erzeugen, etwa eine vollständige Variante für interne Zwecke und eine bereinigte Variante, die ausschließlich @public-Symbole enthält und an Konsumenten ausgeliefert wird. So bleibt die interne Komplexität einer TypeScript-Bibliothek für Konsumenten vollständig unsichtbar.


/**
 * Executes the built query against the configured connection.
 * @public
 */
export class QueryBuilder {
  /**
   * Applies an experimental query hint. Signature may change
   * before this reaches @public status.
   * @beta
   */
  withHint(hint: QueryHint): this {
    // ...
    return this;
  }
}

/**
 * @internal
 */
export function _normalizeDialectName(input: string): string {
  return input.trim().toLowerCase();
}

7. Integration mit API Documenter für Markdown-Docs

Das im vorherigen Schritt erzeugte API Doc Model ist die Eingabe für api-documenter, ein separates Kommandozeilenwerkzeug, das daraus automatisch Markdown-Dateien für jede exportierte Klasse, Schnittstelle und Funktion generiert. Diese generierten Dateien enthalten Signaturen, JSDoc-Beschreibungen, Parameter-Tabellen und Verlinkungen zwischen zusammenhängenden Symbolen, ohne dass ein Entwickler die Dokumentation manuell pflegen muss.

Der entscheidende Vorteil gegenüber handgeschriebener Dokumentation: die generierte Dokumentation kann niemals von der tatsächlichen API abweichen, weil sie direkt aus dem kompilierten Code erzeugt wird. Änderungen an Signaturen oder JSDoc-Kommentaren erscheinen automatisch in der nächsten generierten Doku, ohne dass jemand daran denken muss, eine separate Dokumentationsdatei zu aktualisieren.


# Generate the API doc model first, then render Markdown from it
npx api-extractor run --local
npx api-documenter markdown --input-folder temp --output-folder docs/api

# docs/api now contains one .md file per exported symbol,
# e.g. docs/api/query-builder.querybuilder.where.md

8. CI-Pipeline: run --local vs. CI-Modus

API Extractor unterscheidet zwei Ausführungsmodi, die für unterschiedliche Zwecke gedacht sind. api-extractor run --local aktualisiert den API-Report direkt auf der Festplatte und ist für die lokale Entwicklung gedacht, wenn ein Entwickler bewusst eine API-Änderung vorgenommen hat und den Report entsprechend anpassen möchte. Der einfache Aufruf api-extractor run ohne --local dagegen vergleicht nur und schlägt mit einem Fehlercode fehl, sobald eine Abweichung existiert, ohne die Datei zu verändern.

In der CI-Pipeline gehört ausschließlich der zweite Modus eingesetzt, denn eine automatisch aktualisierte Report-Datei in CI würde den gesamten Sinn des Contract-Tests aushebeln: jede Änderung müsste dann von einem Menschen bewusst reviewt und lokal bestätigt werden, bevor sie überhaupt den Weg in den Pull Request findet. Das erzwingt die bewusste Auseinandersetzung mit jeder API-Änderung, statt sie stillschweigend durchzuwinken.


# .github/workflows/api-contract.yml — compare-only mode, never --local
name: API Contract Check
on: [pull_request]
jobs:
  api-report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm ci
      - run: npm run build
      - run: npx api-extractor run --verbose
        # fails with a non-zero exit code if etc/*.api.md would change

9. API Extractor im Vergleich zu manuellen Alternativen

Ohne ein dediziertes Werkzeug wie API Extractor bleiben nur manuelle oder halbautomatische Alternativen, die deutlich weniger zuverlässig sind. Die folgende Tabelle stellt die Optionen gegenüber.

Ansatz Erkennt Breaking Changes Aufwand
Manuelles Code-Review Unzuverlässig, menschlicher Faktor Hoch, bei jedem Review erneut
tsc --declaration allein Nein, kein Vergleichsmechanismus Gering, aber unvollständig
Eigenes Diff-Skript Teilweise, je nach Implementierung Hoch, Eigenentwicklung
API Extractor Ja, zuverlässig und automatisiert Gering, einmalige Konfiguration

Der einmalige Konfigurationsaufwand von API Extractor amortisiert sich bereits nach wenigen Pull Requests, sobald die erste unbeabsichtigte API-Änderung frühzeitig durch den automatisierten Report-Vergleich auffällt, statt erst als Konsumenten-Bugreport Wochen später.

Mironsoft

TypeScript-Bibliotheken, API-Governance und automatisierte Dokumentation

API Extractor in eurer Bibliothek einrichten lassen?

Wir konfigurieren API Extractor, richten den API-Report als Contract-Test in eurer CI-Pipeline ein und verbinden ihn mit automatisch generierter Dokumentation über API Documenter.

Konfiguration

api-extractor.json und Release-Tag-Strategie für eure Bibliothek aufsetzen

CI-Integration

API-Report als automatisierten Contract-Test in bestehende Pipelines einbauen

Dokumentation

API Documenter für stets aktuelle, automatisch generierte Markdown-Docs einrichten

10. Zusammenfassung

API Extractor verwandelt eine unübersichtliche Sammlung generierter .d.ts-Dateien in eine einzige, kontrollierte öffentliche API-Oberfläche. Der Rollup fasst alle exportierten Symbole zusammen, der API-Report macht jede Änderung an dieser Oberfläche als Diff im Pull Request sichtbar, und Release Tags wie @public, @beta und @internal geben jedem exportierten Symbol einen klaren Status mit entsprechenden Backwards-Compatibility-Erwartungen.

Die Integration mit API Documenter erzeugt zusätzlich stets aktuelle Markdown-Dokumentation direkt aus dem kompilierten Code, ohne manuellen Pflegeaufwand. In der CI-Pipeline sorgt der reine Vergleichsmodus ohne --local dafür, dass jede API-Änderung bewusst reviewt werden muss, bevor sie in einen Release gelangt, statt sich unbemerkt einzuschleichen.

API Extractor für TypeScript-Bibliotheken — Das Wichtigste auf einen Blick

Rollup

Fasst viele generierte .d.ts-Dateien zu einer einzigen, kontrollierten öffentlichen Deklaration zusammen.

API-Report

Eingecheckte .api.md-Datei fungiert als Contract-Test, jede Abweichung schlägt den CI-Build fehl.

Release Tags

@public, @beta, @alpha und @internal geben jedem Symbol klare Backwards-Compatibility-Erwartungen.

Dokumentation

API Documenter erzeugt stets aktuelle Markdown-Docs direkt aus dem Doc Model.

11. FAQ: API Extractor für TypeScript-Bibliotheken

1Wofür ist der Rollup gedacht?
Fasst viele generierte .d.ts-Dateien zu einer öffentlichen Deklaration zusammen, ohne interne Details preiszugeben.
2Was passiert bei Report-Abweichung?
Der CI-Build schlägt fehl, die Datei muss lokal aktualisiert und bewusst in den Pull Request aufgenommen werden.
3Was bedeutet ae-missing-release-tag?
Ein exportiertes Symbol hat keinen Release Tag, API Extractor kann so den Konsumenten-Status nicht bestimmen.
4Unterschied @beta und @public?
@public ist stabil mit vollen Garantien, @beta darf sich ohne Major-Release noch ändern.
5Wann ist ae-forgotten-export ein Problem?
Wenn ein öffentliches Symbol einen nicht exportierten internen Typ referenziert, der dann nicht importierbar ist.
6Wie generiere ich Docs aus dem Doc Model?
Mit api-documenter markdown, das automatisch Markdown-Dateien aus dem JSON-Doc-Model erzeugt.
7Warum kein --local in CI?
Weil es die Report-Datei automatisch aktualisiert und API-Änderungen unbemerkt durchwinken würde.
8Lohnt sich das für kleine Bibliotheken?
Weniger stark, aber die einmalige Konfiguration ist günstig genug, um schon ab mittlerer Größe zu profitieren.
9Mehrere Entry-Points verwaltbar?
Ja, mit mehreren api-extractor.json-Dateien jeweils mit eigenem mainEntryPointFilePath.
10Ersetzt es Unit-Tests?
Nein, es ergänzt sie und prüft ausschließlich die öffentliche Typoberfläche, nicht das Laufzeitverhalten.