Options#
| Option | Type | Default | Description |
|---|---|---|---|
output | Output | { path: 'types', barrel: { type: 'named' } } | Where the generated files are written and exported |
group | Group | — | Split output into per-tag or per-path folders |
enum | EnumOptions | { type: 'asConst', … } | How enums are generated and cased |
syntaxType | 'type' | 'interface' | 'type' | Emit object schemas as type aliases or interfaces |
optionalType | 'questionToken' | 'undefined' | 'questionTokenAndUndefined' | 'questionToken' | How optional properties are written |
arrayType | 'array' | 'generic' | 'array' | Type[] or Array<Type> |
comments | 'full' | 'brief' | 'none' | 'full' | How much of each description reaches the JSDoc |
include | Array<Include> | — | Keep only operations that match |
exclude | Array<Exclude> | [] | Skip operations that match |
override | Array<Override> | [] | Apply different options per pattern |
resolver | ResolverPatch<ResolverTs> | — | Customize generated names and file paths |
macros | Array<Macro> | — | Rewrite AST nodes before printing |
printer | { nodes?: PrinterTsNodes } | — | Replace the handler for a schema type |
output#
Where the generated .ts files are written and how they are exported.
output.path#
Folder where the plugin writes its files (string, default 'types'), resolved against the global output.path on defineConfig.
output.mode#
How generated code is consolidated into files.
'file'writes everything into a single file, sooutput.pathneeds a file extension such as'types.ts'.'directory'writes one file per operation or schema underoutput.path.
Leave it unset and Kubb reads output.path: a name with an extension means one file, anything else a directory.
output.barrel#
output.banner#
output.footer#
group#
group.name#
Turns a group key into a folder or identifier name, used as the subdirectory name and as a suffix on aggregate files. Type (context: { group: string }) => string, default ({ group }) => camelCase(group), which for type: 'path' groups uses the first URL segment as-is instead of camelCasing.
enum#
How OpenAPI enums are represented in the generated TypeScript, and how their names are cased.
enum.type#
Representation of each enum. Defaults to 'asConst'.
'asConst'emits anas constobject plus a key/value type. Tree-shakeable, with no runtime.'enum'emits a TypeScriptenumwith JavaScript runtime code.'constEnum'emits aconst enum, inlined at compile time and incompatible with--isolatedModules.'literal'emits a union type with no runtime value.'inlineLiteral'inlines the union at each usage site instead of giving it a name.
::: code-group
export const petStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatusKey = (typeof petStatus)[keyof typeof petStatus]
export enum PetStatus {
available = 'available',
pending = 'pending',
sold = 'sold',
}
export const enum PetStatus {
available = 'available',
pending = 'pending',
sold = 'sold',
}
export type PetStatus = 'available' | 'pending' | 'sold'
export type PetStatus = 'available' | 'pending' | 'sold'
:::
enum.constCasing#
Casing of the generated const variable when type is 'asConst'. Defaults to 'camelCase'.
'camelCase'names the constpetStatus.'pascalCase'names the constPetStatus, matching the schema name.
::: code-group
export const petStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatusKey = (typeof petStatus)[keyof typeof petStatus]
export const PetStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatusKey = (typeof PetStatus)[keyof typeof PetStatus]
:::
enum.typeSuffix#
Suffix on the type alias generated when type is 'asConst' (string, default 'Key'), applied only to the companion type alias, not the const object name. Set it to '' to drop the suffix, which with constCasing: 'pascalCase' merges the const and type under one name.
::: code-group
export const petStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatusKey = (typeof petStatus)[keyof typeof petStatus]
export const petStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatusValue = (typeof petStatus)[keyof typeof petStatus]
export const petStatus = {
available: 'available',
pending: 'pending',
sold: 'sold',
} as const
export type PetStatus = (typeof petStatus)[keyof typeof petStatus]
:::
enum.keyCasing#
Casing applied to enum key names, 'none' by default (the raw value from the spec).
| Value | Example key |
|---|---|
'screamingSnakeCase' | ENUM_VALUE |
'snakeCase' | enum_value |
'pascalCase' | EnumValue |
'camelCase' | enumValue |
'none' (default) | as-is |
syntaxType#
Whether object schemas are emitted as type aliases or interface declarations, with type as the safer default. Pick interface only when consumers need declaration merging, which is rare for generated code and covered in Type vs Interface.
::: code-group
export type Pet = {
name: string
}
export interface Pet {
name: string
}
:::
optionalType#
How optional properties are written. Defaults to 'questionToken'.
'questionToken'writestype?: string, so the property may be missing.'undefined'writestype: string | undefined, so it must exist but may beundefined.'questionTokenAndUndefined'writestype?: string | undefined, the strictest form. Use it with"exactOptionalPropertyTypes": true.
::: code-group
export type Pet = {
type?: string
}
export type Pet = {
type: string | undefined
}
export type Pet = {
type?: string | undefined
}
:::
arrayType#
Syntax for array types. Defaults to 'array'.
'array'uses the postfixType[].'generic'usesArray<Type>, which reads better for complex elements likeArray<{ id: number }>.
::: code-group
export type Pet = {
tags: string[]
}
export type Pet = {
tags: Array<string>
}
:::
comments#
How much of each OpenAPI description reaches the JSDoc above generated types. Defaults to 'full'.
'full'emits every description in full, however many paragraphs the spec carries.'brief'keeps the opening sentence. Every other tag stays, so each type keeps its documentation. Abbreviations such ase.g.and unclosed brackets are not mistaken for the end of a sentence. A description that runs on for 150 characters without one is cut at the last word before 120 and ends with an ellipsis. The cut moves back further when it would leave a markdown link or code span half written.'none'emits no JSDoc. The generated-by banner at the top of each file is unaffected.
Descriptions are where the bytes go on a large spec. Generating the OpenAI API leaves JSDoc as a third of everything Kubb writes, so 'brief' trims about 197 KB and 'none' about 1 MB of a 2.76 MB output. Reach for one of those when the size of the generated tree matters more than editor hovers.
::: code-group
/**
* @description The identifier, which can be referenced in API endpoints. Treat it as opaque, since the format changes between releases.
* @type string
*/
id: string
/**
* @description The identifier, which can be referenced in API endpoints.
* @type string
*/
id: string
id: string
:::
include#
exclude#
override#
resolver#
Changes how the plugin names generated files and symbols. Pass a partial patch. Override only the members you want, and anything you omit keeps resolverTs. See Override a resolver for the this context and how a patch layers over the default.
Tip
Inside a method this is the full resolver, so this.default.name(name) reuses the built-in casing.
type ResolverTsPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
param?: {
name?(node: OperationNode, param: ParameterNode): string // → 'DeletePetPathPetId'
path?(node: OperationNode, param: ParameterNode): string // → 'GetPetByIdPath'
query?(node: OperationNode, param: ParameterNode): string // → 'FindPetsByStatusQuery'
headers?(node: OperationNode, param: ParameterNode): string // → 'DeletePetHeaders'
}
response?: {
status?(node: OperationNode, statusCode: StatusCode): string // → 'ListPetsStatus200'
options?(node: OperationNode): string // → 'ListPetsOptions'
responses?(node: OperationNode): string // → 'ListPetsResponses'
response?(node: OperationNode): string // → 'ListPetsResponse'
body?(node: OperationNode): string // → 'CreatePetBody'
}
enum?: {
keyName?(node: { name?: string | null }, enumTypeSuffix?: string): string // → 'PetStatusKey'
}
}
macros#
printer#
Replaces the node handler for a schema type such as 'integer' or 'date' with one that builds its TypeScript AST node. Use this.transform to recurse into nested nodes and this.options to read printer options. The printer guide covers the handler context and how overrides compose with macros.
import ts from 'typescript'
import { pluginTs } from '@kubb/plugin-ts'
pluginTs({
printer: {
nodes: {
date() {
return ts.factory.createTypeReferenceNode('Date', [])
},
integer() {
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword)
},
},
},
})