Annotations
Annotations are metadata declarations that provide additional information about types, interfaces, and properties.
Purpose
Annotations serve multiple purposes:
- UI metadata - Labels, placeholders, descriptions for form generation
- Validation constraints - Min/max values, patterns, length restrictions
- Database metadata - Collection names, indexes, field strategies
- Documentation - Descriptions and multi-line documentation
- Custom metadata - Any domain-specific information
Where to Apply
Annotations can be applied anywhere:
@meta.description 'User entity' // Interface annotation
export interface User {
@meta.id // Property annotation
id: string
}
@expect.minLength 3 // Type annotation
export type Username = stringAnnotation Inheritance
Type to Property
When a property uses a type with annotations, annotations merge with property having priority:
@expect.minLength 3
@expect.maxLength 20
export type Username = string
export interface User {
@expect.maxLength 15 // Overrides type's maxLength
username: Username // Inherits minLength: 3, gets maxLength: 15
}Property References
When a property references another property, annotations merge in order:
- Final type annotations
- Referenced property annotations
- Current property annotations (highest priority)
Merge Strategies
When annotations are merged (from type inheritance, property references, or ad-hoc annotations), the merge strategy determines how same-named annotations combine:
Replace (default) — Higher-priority annotations replace lower-priority ones entirely:
@expect.min 3
export type PositiveInt = number
export interface Config {
@expect.min 10 // Replaces type's @expect.min 3
threshold: PositiveInt // Result: @expect.min is 10
}Append — Both values are kept, accumulating into an array:
@expect.pattern '^[a-z]+$', '', 'Must be lowercase'
export type SafeString = string
export interface Form {
@expect.pattern '^\S+$', '', 'No spaces'
code: SafeString
// Result: both patterns are validated
}The strategy is configured per annotation via AnnotationSpec. Most annotations use replace. The built-in @expect.pattern uses append.
Repeatable Annotations
Annotations marked with multiple: true can appear more than once on the same node. Their values are stored as arrays:
export interface User {
@expect.pattern '^[A-Z]', '', 'Must start with uppercase'
@expect.pattern '.{3,}', '', 'Must be at least 3 characters'
name: string
}When merged, how repeated annotations combine depends on the merge strategy:
multiple: true+ replace (default) — The higher-priority set replaces the entire arraymultiple: true+ append — Values from both sides are concatenated into a single array
Annotation Syntax
@meta.label 'User Name' // With argument
@meta.sensitive // Flag (no argument)
@expect.pattern "^[A-Z]", "i" // Multiple arguments
@meta.documentation 'Line 1' // Can be repeated
@meta.documentation 'Line 2'Arguments can be optional. Annotations without arguments are flag annotations.
Core Annotations
Atscript provides common-purpose annotations:
Meta Annotations (@meta.*)
@meta.label 'text'- Human-readable label@meta.idor@meta.id 'name'- Marks identifier field@meta.description 'text'- Field description@meta.documentation 'text'- Multi-line docs (repeatable)@meta.placeholder 'text'- UI placeholder text@meta.sensitive- Marks sensitive data@meta.readonly- Read-only field@meta.isKey- Key field in arrays for lookups
Validation Annotations (@expect.*)
@expect.minLength 5- Minimum string/array length@expect.maxLength 100- Maximum string/array length@expect.min 0- Minimum number value@expect.max 100- Maximum number value@expect.int- Must be integer@expect.pattern "regex", "flags", "message"- Pattern validation (repeatable)
Custom Annotations
Unknown Annotations
Support any user-specified annotations by setting in config:
// atscript.config.js
export default {
unknownAnnotation: 'allow' // or 'warn', 'error' (default)
}Defining Custom Annotations
Add custom annotations with IntelliSense support:
// atscript.config.js
import { defineConfig, AnnotationSpec } from '@atscript/core'
export default defineConfig({
annotations: {
ui: {
hidden: new AnnotationSpec({
description: 'Hide field in UI',
nodeType: ['prop']
}),
column: new AnnotationSpec({
description: 'Table column width',
argument: {
name: 'width',
type: 'number'
}
}),
tag: new AnnotationSpec({
description: 'UI display tag',
multiple: true, // Can be repeated
mergeStrategy: 'append', // Accumulates across merges
argument: {
name: 'value',
type: 'string'
}
})
}
}
})Use in .as files:
export interface User {
@ui.hidden
internalId: string
@ui.column 200
name: string
}Plugins can also provide custom annotations through their configuration.
Ad-hoc Annotations
You can attach annotations to an existing type without modifying its definition using the annotate keyword. This supports both mutating (in-place) and non-mutating (alias) forms, including cross-file usage.
See Ad-hoc Annotations for full details.
Next Steps
- Ad-hoc Annotations - Annotate existing types
- Configuration - Configure Atscript
- Build Setup - Integrate with build tools