The typescript generator — its skill
This file is the DESIGN of your ejected typescript generator (generators/typescript/):
to change the generator, edit this skill first, then make the code match it — a diff
to generators/typescript/ that has no covering sentence here is incomplete.
What it emits
The typed TypeScript client itself: model types with JSDoc, type guards, the Ops
type map, the OPERATIONS descriptor table, a client instance, one binding per
operation, and the runtime — embedded in the file (runtime: inline, the default) or
written as real modules in a runtime/ folder beside it that the client imports
relatively (runtime: module).
Design decisions that must hold
- Descriptor-driven: generated code is DATA (
OPERATIONS + Ops) plus wiring;
request behavior lives in the runtime, never in per-operation code.
satisfies Record<string, OperationDescriptor> is the version-skew guard.
single vs split: split derives <stem>.schemas.ts (types, enums, guards) and
an entry that export *s it; the entry type-imports only the schema names it
references (collectEntrySchemaRefs).
- Zero runtime dependencies.
Date, Blob, fetch — nothing else.
- Names are collision-safe:
packageIdents seeds every reserved wiring name before
any operation is sanitized, so renames are deterministic (configure → configure_2).
A rename becomes part of the SDK's public API, so the warning must say WHICH cause it
is and what the publisher can do: a duplicate operationId in the description (fix the
description — the only real fix), a name that isn't a valid identifier, or a clash with
a name the generated module already declares. A vague "collides or is invalid" message
leaves the publisher unable to act.
- One operation, one function, one input shape. The module-level names are bindings
of the client's own methods (
export const { getOrder } = client;), never wrappers, so
getOrder and client.getOrder cannot disagree about their arguments. argsStyle
shapes the method itself: grouped (the default) namespaces the inputs by transport
layer — path, query, headers, cookies, body — and flat merges them into one
object, which the runtime converts back using the descriptor's own parameter list. An
operation whose merged names would collide keeps the grouped shape.
- Throw mode returns the body;
{ envelope: true } opts into
{ data, headers, response } with typed declared headers. Result mode returns
{ data, error, response } and ignores envelope.
The stage files
One file per stage of the emit, same skeleton as the other generators:
types.ts renders type text (tsType, JSDoc, the model type aliases);
operations.ts the per-operation surface (the Ops map, the <Op>* aliases, the
input shapes, flatInputShape);
descriptor.ts the OPERATIONS wire table and the collision-safe packageIdents;
client.ts the assembly (single/split entries, the runtime needs, the module-mode
runtime files);
type-guards.ts, response-headers.ts, operation-types.ts, operation-signature.ts
the narrower questions their names state;
banner.ts the generated-by header and title comment;
inline-runtime.ts the runtime assembly for both modes. The runtime's real sources ship
inside the package and reach the generator through
@redocly/client-generator/runtime-sources (in this repo they live in runtime/ beside
these files). Naming and string escaping live in the TypeScript printer
(@redocly/client-generator/printers/typescript).
Ejecting it
redocly eject-generator typescript copies this generator's TypeScript source folder to
generators/typescript/ — the stage files above, exactly as we wrote them. Imports stay
package specifiers: @redocly/client-generator (the toolkit and IR types),
@redocly/client-generator/printers/typescript (naming and text mechanics), and
@redocly/client-generator/runtime-sources (the runtime sources it embeds). Running a
.ts generator uses Node's own type stripping (Node 22.18, 23.6, or newer), and newer
built-in versions merge into your copy per file with
redocly eject-generator typescript --update.
It is the largest of them, so reach for the smaller paths first when they fit:
client.setup bakes publisher defaults into the generated client, and middleware or
configure() change behavior at run time rather than generation time.
- It documents itself. With
client.docs (or --docs), the docs hook writes
<stem>.typescript.md: the security schemes, then one section per operation with its parameters,
body, response type, and behavior notes. The call snippets come from this generator's own
sample hook, so the page can only show the syntax of the SDK beside it, and the layout
comes from renderReferencePage in the authoring toolkit — reachable from an ejected copy
through @redocly/client-generator. Pagination on the page is decided by
paginationRuleFor, the same helper this generator resolves pagination with.
The modify loop
- Edit this skill: state the new behavior or decision.
- Make
generators/typescript/ match it.
- Run
redocly generate-client and inspect the git diff of the generated output —
generated files are never hand-edited.
Newer built-in versions merge in with redocly eject-generator typescript --update.
1---2name: typescript-generator3description: Design of the ejected Redocly `typescript` client generator. Read it, and update it, before changing generators/typescript/.4---56# The `typescript` generator — its skill78This file is the DESIGN of your ejected `typescript` generator (`generators/typescript/`):9**to change the generator, edit this skill first, then make the code match it** — a diff10to `generators/typescript/` that has no covering sentence here is incomplete.1112## What it emits1314The typed TypeScript client itself: model types with JSDoc, type guards, the `Ops`15type map, the `OPERATIONS` descriptor table, a `client` instance, one binding per16operation, and the runtime — embedded in the file (`runtime: inline`, the default) or17written as real modules in a `runtime/` folder beside it that the client imports18relatively (`runtime: module`).1920## Design decisions that must hold2122- **Descriptor-driven:** generated code is DATA (`OPERATIONS` + `Ops`) plus wiring;23 request behavior lives in the runtime, never in per-operation code.24 `satisfies Record<string, OperationDescriptor>` is the version-skew guard.25- **`single` vs `split`:** split derives `<stem>.schemas.ts` (types, enums, guards) and26 an entry that `export *`s it; the entry type-imports only the schema names it27 references (`collectEntrySchemaRefs`).28- **Zero runtime dependencies.** `Date`, `Blob`, `fetch` — nothing else.29- **Names are collision-safe:** `packageIdents` seeds every reserved wiring name before30 any operation is sanitized, so renames are deterministic (`configure` → `configure_2`).31 A rename becomes part of the SDK's public API, so the warning must say WHICH cause it32 is and what the publisher can do: a duplicate `operationId` in the description (fix the33 description — the only real fix), a name that isn't a valid identifier, or a clash with34 a name the generated module already declares. A vague "collides or is invalid" message35 leaves the publisher unable to act.36- **One operation, one function, one input shape.** The module-level names are bindings37 of the client's own methods (`export const { getOrder } = client;`), never wrappers, so38 `getOrder` and `client.getOrder` cannot disagree about their arguments. `argsStyle`39 shapes the method itself: `grouped` (the default) namespaces the inputs by transport40 layer — `path`, `query`, `headers`, `cookies`, `body` — and `flat` merges them into one41 object, which the runtime converts back using the descriptor's own parameter list. An42 operation whose merged names would collide keeps the grouped shape.43- **Throw mode returns the body**; `{ envelope: true }` opts into44 `{ data, headers, response }` with typed declared headers. Result mode returns45 `{ data, error, response }` and ignores `envelope`.4647## The stage files4849One file per stage of the emit, same skeleton as the other generators:50`types.ts` renders type text (`tsType`, JSDoc, the model type aliases);51`operations.ts` the per-operation surface (the `Ops` map, the `<Op>*` aliases, the52input shapes, `flatInputShape`);53`descriptor.ts` the `OPERATIONS` wire table and the collision-safe `packageIdents`;54`client.ts` the assembly (single/split entries, the runtime needs, the module-mode55runtime files);56`type-guards.ts`, `response-headers.ts`, `operation-types.ts`, `operation-signature.ts`57the narrower questions their names state;58`banner.ts` the generated-by header and title comment;59`inline-runtime.ts` the runtime assembly for both modes. The runtime's real sources ship60inside the package and reach the generator through61`@redocly/client-generator/runtime-sources` (in this repo they live in `runtime/` beside62these files). Naming and string escaping live in the TypeScript printer63(`@redocly/client-generator/printers/typescript`).6465## Ejecting it6667`redocly eject-generator typescript` copies this generator's TypeScript source folder to68`generators/typescript/` — the stage files above, exactly as we wrote them. Imports stay69package specifiers: `@redocly/client-generator` (the toolkit and IR types),70`@redocly/client-generator/printers/typescript` (naming and text mechanics), and71`@redocly/client-generator/runtime-sources` (the runtime sources it embeds). Running a72`.ts` generator uses Node's own type stripping (Node 22.18, 23.6, or newer), and newer73built-in versions merge into your copy per file with74`redocly eject-generator typescript --update`.7576It is the largest of them, so reach for the smaller paths first when they fit:77`client.setup` bakes publisher defaults into the generated client, and middleware or78`configure()` change behavior at run time rather than generation time.7980- **It documents itself.** With `client.docs` (or `--docs`), the `docs` hook writes81 `<stem>.typescript.md`: the security schemes, then one section per operation with its parameters,82 body, response type, and behavior notes. The call snippets come from this generator's own83 `sample` hook, so the page can only show the syntax of the SDK beside it, and the layout84 comes from `renderReferencePage` in the authoring toolkit — reachable from an ejected copy85 through `@redocly/client-generator`. Pagination on the page is decided by86 `paginationRuleFor`, the same helper this generator resolves pagination with.8788## The modify loop89901. Edit this skill: state the new behavior or decision.912. Make `generators/typescript/` match it.923. Run `redocly generate-client` and inspect the `git diff` of the generated output —93 generated files are never hand-edited.9495Newer built-in versions merge in with `redocly eject-generator typescript --update`.