Skip to content

Code Generation

The TypeScript plugin generates code from .as files in two formats: .d.ts for type checking and .js for runtime metadata.

DTS Format

The default format. Generates TypeScript declaration files for static type checking and IDE support.

Given this .as file:

atscript
@meta.description 'A product in the catalog'
export interface Product {
    @meta.label 'Product Name'
    @expect.minLength 3
    name: string

    @expect.min 0
    price: number

    inStock: boolean
}

The generated .d.ts:

typescript
export declare class Product {
  name: string
  price: number
  inStock: boolean
  static __is_atscript_annotated_type: true
  static type: TAtscriptTypeObject<keyof Product, Product>
  static metadata: TMetadataMap<AtscriptMetadata>
  static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof Product>
  static toJsonSchema: () => any
  static toExampleData?: () => any
}
export declare namespace Product {
  type DataType = Product
}

Key points:

  • Interfaces become declare class with instance properties and static members
  • The static members provide access to type metadata, validation, JSON Schema, and example data at runtime
  • toExampleData is always optional — when exampleData: true is set in plugin options, it's rendered without deprecation; otherwise it's marked @deprecated
  • Product.DataType is a type alias for the data shape — useful for generic utilities

Interface Extends

When an interface uses extends, the first parent becomes the TypeScript extends target. Properties from additional parents and own properties are rendered in the class body:

atscript
interface Base {
    id: string
    createdAt: string
}

interface Auditable {
    updatedBy: string
}

export interface Post extends Base, Auditable {
    title: string
}

Generates:

typescript
export declare class Post extends Base {
  updatedBy: string  // from Auditable (second parent, inlined)
  title: string      // own prop
  // id and createdAt come via TS extends from Base
  static __is_atscript_annotated_type: true
  // ... static members
}

The JS output resolves all parent properties into a single merged type tree, so runtime metadata includes all inherited props and their annotations.

For types (not interfaces), a companion namespace carries the same static members:

typescript
export type Status = 'active' | 'inactive'
declare namespace Status {
  type DataType = Status
  const __is_atscript_annotated_type: true
  const type: TAtscriptTypeComplex<Status>
  const metadata: TMetadataMap<AtscriptMetadata>
  const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, Status>
  const toJsonSchema: () => any
  const toExampleData: (() => any) | undefined
}

JS Format

Generates JavaScript files with full runtime metadata. Use this when you need validation, metadata access, or serialization.

The same Product .as file generates:

javascript
import {
  defineAnnotatedType as $,
  annotate as $a,
  buildJsonSchema as $$,           // only with jsonSchema: 'lazy'
  createDataFromAnnotatedType as $e, // only with exampleData: true
  throwFeatureDisabled as $d,       // only with jsonSchema: false (default)
} from '@atscript/typescript/utils'

export class Product {
  static __is_atscript_annotated_type = true
  static type = {}
  static metadata = new Map()
  static id = 'Product'
  static toJsonSchema() {
    return this._jsonSchema ?? (this._jsonSchema = $$(this))
  }
  static toExampleData() {
    return $e(this, { mode: 'example' })
  }
}

$('object', Product)
  .prop(
    'name',
    $()
      .designType('string')
      .tags('string')
      .annotate('meta.label', 'Product Name')
      .annotate('expect.minLength', 3).$type
  )
  .prop('price', $().designType('number').tags('number').annotate('expect.min', 0).$type)
  .prop('inStock', $().designType('boolean').tags('boolean').$type)
  .annotate('meta.description', 'A product in the catalog')

The defineAnnotatedType() calls build the type structure and attach all annotations as runtime metadata. Semantic types (like string.email) automatically add validation rules and tags.

Imports depend on config

The example above shows imports for jsonSchema: 'lazy' and exampleData: true. By default:

  • JSON Schema is disabled (jsonSchema: false) — toJsonSchema() calls throwFeatureDisabled() (aliased as $d) instead, and the buildJsonSchema import is omitted. With jsonSchema: 'bundle', the schema is pre-computed and embedded as a static return value (no import needed).
  • Example Data is disabled (exampleData: false) — toExampleData() is not rendered at all, and the createDataFromAnnotatedType import is omitted.

See Configuration for details.

When to Use Which

.d.ts.js
Type checkingYesYes (with .d.ts alongside)
IDE IntelliSenseYesYes
Runtime validationNeeds .jsYes
Metadata accessNeeds .jsYes
JSON SchemaNeeds .jsYes
Bundle sizeZero (types only)Includes runtime metadata

In practice, use .d.ts during development (generated by VSCode extension on save) and .js for builds that need runtime features.

Next Steps

  • CLI — build .as files from the command line
  • Type Definitions — understand the generated type structure
  • Validation — validate data against generated types

Released under the MIT License.