Skip to content

TypeScript

The typescript format generates TypeScript interfaces, type aliases, and enums from OpenAPI schemas.

Basic Example

OpenAPI Schema:

yaml
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:

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

jsonc
{
  "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.

typescript
export interface User {
  id: string;
  name: string;
}
typescript
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.

typescript
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.

typescript
export type Status = "active" | "inactive" | "pending";
typescript
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 TypeOpenAPI FormatTypeScript
string-string
stringdatestring
stringdate-timestring
stringemailstring
stringuristring
stringuuidstring
integer-number
integerint32number
integerint64number
number-number
numberfloatnumber
numberdoublenumber
boolean-boolean
array-T[]
object-interface / type
object(no properties)Record<string, unknown>

Enums

By default, string enums generate union types:

typescript
export type Status = "active" | "inactive" | "pending";

With generateEnums: true (global option):

typescript
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:

typescript
export interface User {
  name?: string;  // Optional
}
typescript
export interface User {
  name: string | null;  // Nullable
}
typescript
export interface User {
  name?: string | null;  // Optional and nullable
}

References

$ref references generate type references:

OpenAPI:

yaml
properties:
  author:
    $ref: '#/components/schemas/User'

Generated:

typescript
export interface Post {
  author: User;
}

With dereferenceRefs: true, the referenced type is inlined instead.

Composition

allOf (Intersection)

yaml
allOf:
  - $ref: '#/components/schemas/BaseEntity'
  - type: object
    properties:
      name:
        type: string
typescript
export type Product = BaseEntity & {
  name: string;
};

oneOf / anyOf (Union)

yaml
oneOf:
  - $ref: '#/components/schemas/Dog'
  - $ref: '#/components/schemas/Cat'
typescript
export type Pet = Dog | Cat;

Namespaces

Group types into TypeScript namespaces:

jsonc
{
  "contents": [
    {
      "namespace": "Models",
      "contents": ["api:User", "api:Product"]
    },
    {
      "namespace": "Requests",
      "contents": ["api:*Request"]
    }
  ]
}

Generated:

typescript
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:

typescript
export namespace Requests {
  export interface CreateOrderRequest {
    product: Models.Product;  // Correct namespace path
  }
}

JSDoc Comments

Schema descriptions become JSDoc comments:

yaml
properties:
  id:
    type: string
    description: Unique identifier for the user
typescript
export interface User {
  /** Unique identifier for the user */
  id: string;
}

Best Practices

File Organization

For large APIs, split by domain:

jsonc
"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:

typescript
// 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

Released under the MIT License.