TypeScript
The typescript format generates TypeScript interfaces, type aliases, and enums from OpenAPI schemas.
Basic Example
OpenAPI Schema:
components:
schemas:
User:
type: object
required:
- id
- email
properties:
id:
type: string
description: Unique identifier
email:
type: string
format: email
name:
type: string
role:
$ref: '#/components/schemas/UserRole'
UserRole:
type: string
enum: [admin, user, guest]Generated TypeScript:
// Generated by an automated build step. Do not edit.
export interface User {
/** Unique identifier */
id: string;
email: string;
name?: string;
role?: UserRole;
}
export type UserRole = "admin" | "user" | "guest";Configuration
{
"outputs": [
{
"path": "./src/types/api.ts",
"format": "typescript",
"contents": ["api:*"],
"options": {
"exportType": "interface",
"readonlyProperties": false,
"widenEnums": false
}
}
]
}Options
exportType
Type: "interface" | "type"
Default: "interface"
Controls how object types are exported.
export interface User {
id: string;
name: string;
}export type User = {
id: string;
name: string;
};When to use type:
- You need mapped types or conditional types
- Consistency with existing codebase conventions
- Working with intersection types
When to use interface:
- Better error messages in most cases
- Declaration merging support
- Slightly better IDE performance for large types
readonlyProperties
Type: boolean
Default: false
Adds readonly modifier to all properties.
export interface User {
readonly id: string;
readonly name: string;
}Useful for immutable data patterns or preventing accidental mutations.
widenEnums
Type: boolean
Default: false
Adds | (string & {}) to string enum union types for forward compatibility. This allows any string value while still providing autocomplete for known enum values.
export type Status = "active" | "inactive" | "pending";export type Status = "active" | "inactive" | "pending" | (string & {});This is useful when:
- The API may add new enum values in the future
- You want to accept arbitrary strings while still documenting known values
- You're integrating with APIs that don't strictly enforce enum values
The (string & {}) trick preserves TypeScript's autocomplete suggestions for the literal values while allowing any string to be assigned.
WARNING
This option only applies when generateEnums is false (the default). When using TypeScript enums, widening is not applicable.
Type Mapping
| OpenAPI Type | OpenAPI Format | TypeScript |
|---|---|---|
string | - | string |
string | date | string |
string | date-time | string |
string | email | string |
string | uri | string |
string | uuid | string |
integer | - | number |
integer | int32 | number |
integer | int64 | number |
number | - | number |
number | float | number |
number | double | number |
boolean | - | boolean |
array | - | T[] |
object | - | interface / type |
object | (no properties) | Record<string, unknown> |
Enums
By default, string enums generate union types:
export type Status = "active" | "inactive" | "pending";With generateEnums: true (global option):
export enum Status {
Active = "active",
Inactive = "inactive",
Pending = "pending"
}Enum keys are derived from values using PascalCase transformation.
Nullable Fields
Controlled by the global nullableHandling option:
export interface User {
name?: string; // Optional
}export interface User {
name: string | null; // Nullable
}export interface User {
name?: string | null; // Optional and nullable
}References
$ref references generate type references:
OpenAPI:
properties:
author:
$ref: '#/components/schemas/User'Generated:
export interface Post {
author: User;
}With dereferenceRefs: true, the referenced type is inlined instead.
Composition
allOf (Intersection)
allOf:
- $ref: '#/components/schemas/BaseEntity'
- type: object
properties:
name:
type: stringexport type Product = BaseEntity & {
name: string;
};oneOf / anyOf (Union)
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'export type Pet = Dog | Cat;Namespaces
Group types into TypeScript namespaces:
{
"contents": [
{
"namespace": "Models",
"contents": ["api:User", "api:Product"]
},
{
"namespace": "Requests",
"contents": ["api:*Request"]
}
]
}Generated:
export namespace Models {
export interface User {
id: string;
name: string;
}
export interface Product {
id: string;
title: string;
}
}
export namespace Requests {
export interface CreateUserRequest {
name: string;
email: string;
}
}Cross-namespace references are automatically resolved:
export namespace Requests {
export interface CreateOrderRequest {
product: Models.Product; // Correct namespace path
}
}JSDoc Comments
Schema descriptions become JSDoc comments:
properties:
id:
type: string
description: Unique identifier for the userexport interface User {
/** Unique identifier for the user */
id: string;
}Best Practices
File Organization
For large APIs, split by domain:
"outputs": [
{ "path": "./types/users.ts", "contents": ["api:User*"] },
{ "path": "./types/products.ts", "contents": ["api:Product*"] },
{ "path": "./types/orders.ts", "contents": ["api:Order*"] }
]Re-exporting
Create an index file for convenient imports:
// types/index.ts
export * from './users';
export * from './products';
export * from './orders';Type Guards
sparktype generates type definitions only. For runtime type guards, consider using the Zod format alongside TypeScript.
See Also
- Zod Format - Add runtime validation
- Configuration Options
- Namespaces